Click here to Skip to main content
15,911,317 members
Articles / Programming Languages / XML
Article

Layering Windows Application in .NET (WinForms) Part I

Rate me:
Please Sign up or sign in to vote.
3.02/5 (22 votes)
13 Jul 20058 min read 113.2K   804   66   22
Layering Windows UI layer using inheritance.

Introduction

This article explains the advantages of abstracting the various layers during the time of Rapid Application Development (RAD). Everybody would have got a chance to work on ERP applications. A client who needs ERP products prefers UI of type Windows application. This is mainly because of the fact that Windows applications are quick in response and also have many sophisticated controls. ERP applications expect uniformity in UI across forms as one its key requirements. The uniformity in UI improves the productivity of the end users who use the ERP application. The major problem in an ERP application source is that the code written will become unmanageable after some time and it also becomes hard to extend. In this article, we will discuss how OOPS helps us to achieve some basic UI layering, which helps us to develop rapid ERP applications which are uniform in their look.

Layering of Windows Controls

First and foremost step in the product development life cycle is abstracting the Windows controls which we are going to program around. As a thumb rule, we should always create a wrapper class (class inherits from the base class) of all the controls which we re going to use in the project and publish the assembly as a UI control library to all the developers in the project. This is applicable to only those controls which are inheritable. Most of the controls available are inheritable except a few like FileOpenDialog etc. are not inheritable. This helps us to have one centralised place of control over thousands of forms using n number of controls. Initially, we might not understand the advantages of doing this. But this layering helps only in the long run. All the WinForm controls are well defined and the signatures are well published and coded in OOPS. So they are by default extensible through inheritance and overriding.

As a Proof Of Concept (POC), I would like to explain one of the key features of extending a control. In all our forms which we develop, we need to perform some basic non-business validations like email validations, mathematical calculation validations etc. Normally these types of validations will be done in the textbox validating event. In this POC, I would like to highlight the powerfulness of Regular Expressions and the layering of Windows controls. Let us re-define our problem statement. Regular Expressions are a very powerful library which helps us to perform pattern matching validations. In simple terms, we can define Regular Expressions as a formula which evaluates for a value which returns true or false. True means that the value evaluated adheres to the formula pattern. False means the value evaluated fails in the pattern match. TextBox is one of the most widely used controls which takes user inputs. Now we will extend the text box to support regular expression validations.

VB
Imports System.Text 
Public Class TextBoxBase 
       Inherits System.Windows.Forms.TextBox 
    Private _RegularExpression As String = "" 
    Private _ErrorMessage As String = "" 
    Private ErrControl As New ErrorProvider 

Public Property RegExToValidate() As String 
    Get 
        Return _RegularExpression 
    End Get 
    Set(ByVal Value As String) 
         _RegularExpression = Value 
     End Set 
 End Property 

Public Property ErrorMessage() As String 
    Get 
         Return _ErrorMessage 
    End Get 
    Set(ByVal Value As String) 
        _ErrorMessage = Value 
    End Set 
End Property 

Private Sub TextBoxBase_Validating(ByVal sender As Object, _
            ByVal e As System.ComponentModel.CancelEventArgs) _
            Handles MyBase.Validating 
  Try 
       If _RegularExpression.Length > 0 Then 
           If RegularExpressions.Regex.IsMatch(Me.Text, _
                         _RegularExpression) = False Then 
                 ErrControl.SetError(Me, _ErrorMessage) e.Cancel = True 
            Else 
                 ErrControl.SetError(Me, Nothing) 
            End If
         End If 
   Catch ex As Exception 
       ' Do Exception Management Here. 
   End Try 
End Sub 
End Class

Sample Image

Sample Image

In the above example, the extension is very simple to make the article easy to understand. Ideally speaking, we will give much more configurations like, validating the textbox data based on multiple regular expressions, and also we should be able to configure the validations as AND/OR conditions. This will help us to have a very strong validation layer with various options. I don't want to cover the entire configuration, because in this, we are not trying to explain the complexity of the functionality, instead we are trying to explain the advantages of controls abstraction.

Abstracting UI Layer

Abstracting the Windows controls is the starting point in UI abstraction. Abstracting the controls helps us to extend the control's functionality in one place and it will be available to all the controls inheriting in the same hierarchy tree. Abstracting the UI layer is nothing but a flexible design on which the entire ERP form development happens. The main point which should be there in our mind during the design is that, the forms should be pluggable and also have a uniform look. There are various designs available to achieve this. Before getting to the design part, I would like to give some basic thumb rule which we need to keep in our mind.

  1. Never have a static menu system which is hard wired in code. Have a dynamic menu system based on some data store. Data store might be an XML file or a database. This helps us add/remove menus without touching the code.
  2. Never allow the forms to inherit from the base .NET Windows class (System.Windows.Forms.Form). Instead always have one level of inherited wrapper class.
  3. Never commit a signature on a class implementation, instead commit on an interface.
  4. Let form instantiation happen in a single place, and always float a static context object across the forms. This gives us a way to have some communication between the loaded forms. This is applicable in MDI architecture where the loaded child forms can communicate. The context object can hold all the necessary data which needs to be shared by all the forms in the system. The context object acts as a holder of meta data of the system.

In the design which we made for this article, we have used XML file as the data store to store the menu information. Each and every menu item will carry AssemblyFilePath and TypeName to locate the form. All the forms which we are going to develop will not be of type Form, instead it will be of type UserControl. In runtime, the UI layer will render the output of the control in a form. Whenever the form gets loaded, an ApplicationContext class implementation of IApplicationContext is getting passed to all the forms which are getting loaded. Source code of the UI layer is available for download in the article. So I am not planning to explain the internal classes which are used in the UI layer, instead I am going to explain steps to be followed to develop a form using this architecture.

Form Development Steps

  1. Install the UI layer package to a specified location.
  2. Create a WinForm project and make the project as class library. Name the project as MyERPApplication.

    Sample Image

  3. Point the project start application to the path of MainUI.exe. MainUI.exe will be available in the installation path of the package.

    Sample Image

  4. Add reference to WinformUIAbstraction.dll. WinformUIAbstraction will be available in the installation folder.
  5. Create a new WinForm and name the form as Form1.
  6. Go to the code view of the form Form1 and the change the inheritance from System.Windows.Forms.Form to WinformUIAbstraction.FormBase.
  7. Now if you switch to design mode, you can see the container as a user control.
  8. Now you can place as many controls as you want depending upon your requirement.
  9. Change the Project Output to the Package installation directory.

    Sample Image

Registering Menus

  1. Go to the installation folder and open Menus.XML.
    XML
    <?xml version="1.0" encoding="utf-8" ?>
     <configuration>
        <Menu>
           <SubMenu Name="Main Menu1">
             <Component  ID ="9E8E613F-37C7-4bc9-9A84-4D9298A44D87" 
                      Name="Menu1-Form1" AssemblyPath="MainUI.exe" 
                      TypeName="MainUI.MenuConfigEditor"></Component>
             <Component  ID ="9E8E613F-37C7-4bc9-9A84-4D9298A44D86" 
                      Name="Menu1-Form2" AssemblyPath="MainUI.exe" 
                      TypeName="MainUI.Test"></Component>
           </SubMenu>
        
          <SubMenu Name="Main Menu2">
              <Component ID ="" Name="Form1" AssemblyPath="" 
                      TypeName=""></Component>
          </SubMenu>
        </Menu>
    </configuration>
  2. We can add a new menu by adding a SubMenu node and we can add the actual menu using the Component node.
    XML
    <?xml version="1.0" encoding="utf-8" ?>
    
    <SubMenu Name="<Name of the main menu>">
        <Component ID =""<GUID>" Name=""<Sub Meny Name >" 
               AssemblyPath=""<Path of the assembly>" 
               TypeName=""<Fully Qualified TypeName>"></Component>
        <!-- In our case the component Node will look like -->
        <Component  ID ="9E8E613F-37C7-4bc9-9A84-4D9298A44D86" 
               Name="My First Form" AssemblyPath="MyERPApplication.dll" 
               TypeName="MyERPApplication.Form1"></Component>
    </SubMenu>
  3. Now Run the project. The mainUI.exe will get loaded with the configured menus. If we click on the "My First Form" menu, the user control will get loaded in a form. In runtime, we will not feel that the loading container is a user control, it will look like a form only.

Application Context Object

When ever the form gets loaded, the first method which will get called is OnInit. This method carries an object of type IApplicationContext. This object carries all the meta data of the system like connection string, instances of the forms which are currently loaded in the system etc. We can extend this to store as many information as it can carry. Following interface code tells us about the structure of IApplicationContext interface.

VB
Public Interface IApplicationContext 
    'Connection String Configured in the exe.config.
    ReadOnly Property ConnectionString() As String
    ReadOnly Property SQLConnection() As SqlClient.SqlConnection 
    Function BeginTransaction() As SqlClient.SqlTransaction
    ' Holds all the instances of loaded forms.
    ReadOnly Property FormsCollection()  As FormCollection
    Property MainUI() As Object ' Main UI object (MDI Form)
 End Interface

Forms Collection

Application Context Object has a readonly property called FormsCollection. This class contains all the instances of the forms which are loaded in the UI, by making FormID as the key which is specified in menus.xml. Following is the structure of the FormCollection class.

VB
Public Class FormCollection ' Actual Code Excluded
   Public Shared Function FindForm(ByVal FormID As String) As Form 
   Public Function AddForm(ByVal frm As Form, ByVal FormID As String) As Boolean 
   Public Function RemoveForm(ByVal FormID As String) As Boolean 
End Class

Application Context Object adheres to Singleton pattern

Application context object is one of the good examples of implementation of Singleton pattern. This object should be created only once and the same instance should get passed across forms. Following code snippet explains the minimal implementation of the Application Context class:

VB
Public Class WinApplicationContext 
        Implements IApplicationContext 
Shared _MyInstance As IApplicationContext 

Private Sub New() 'Private Constructor 
End Sub Public 

Shared Function ApplicationContext() As IApplicationContext 
' Shared Property to return the instance. 
  If _MyInstance Is Nothing Then 
    _MyInstance = New WinApplicationContext 
  End 
 Return _MyInstance 
End Function 
End Class

Enhancements

  1. Currently the menu items which we configure always expects a form. It's not necessary that the menu click should result in opening up a form. It can be any thing. Interfaces can be extended to accommodate this.
  2. Menus section can be easily moved to DB for scalability.
  3. Good level of extensions can be done in all the wrapper controls.
  4. Currently the base class is an empty container. Since we are planning for ERP forms which will always have some standard UI templates like "New", "Edit" etc., those controls can get added in the base class and appropriate hooks can be provided to the developers to handle the events. Enabling and disabling of controls can be handled in the base class so that developers need not write code to enable all the buttons during the "New" button click.
  5. Design can also be improved.

History

Has only the layering for UI, which is not configurable. Only menus can get re-directed to a new component. In part II of this article, we will discuss about a design in which all the code which we write is pluggable.

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


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralGood-But all time not satisfy Pin
manoharmind15-Mar-11 18:30
manoharmind15-Mar-11 18:30 
Question[My vote of 1] Whats the point? Pin
aaroncampf24-Nov-10 10:54
aaroncampf24-Nov-10 10:54 
GeneralMy vote of 2 Pin
Dave Kreskowiak27-Jan-10 1:45
mveDave Kreskowiak27-Jan-10 1:45 
GeneralI would like to ask... Pin
alav18-Jun-07 8:38
alav18-Jun-07 8:38 
GeneralClosing child forms issue Pin
SerjM16-Jan-06 22:04
SerjM16-Jan-06 22:04 
GeneralControl canvas cut at bottom and right sides Pin
SerjM16-Jan-06 4:26
SerjM16-Jan-06 4:26 
AnswerRe: Control canvas cut at bottom and right sides Pin
SerjM16-Jan-06 4:47
SerjM16-Jan-06 4:47 
QuestionHow to raise events in WinFormBase Pin
SerjM16-Jan-06 3:34
SerjM16-Jan-06 3:34 
AnswerRe: How to raise events in WinFormBase Pin
SATHISH CG16-Jan-06 19:17
SATHISH CG16-Jan-06 19:17 
GeneralRe: How to raise events in WinFormBase Pin
SerjM16-Jan-06 21:51
SerjM16-Jan-06 21:51 
GeneralImplementation in C# Pin
aksIxion24-Jul-05 22:13
aksIxion24-Jul-05 22:13 
GeneralRe: Implementation in C# Pin
SATHISH CG25-Jul-05 23:55
SATHISH CG25-Jul-05 23:55 
GeneralRe: Implementation in C# Pin
aksIxion26-Jul-05 1:07
aksIxion26-Jul-05 1:07 
GeneralWant your advise... Pin
Mo7ammed_Farid15-Jul-05 5:34
sussMo7ammed_Farid15-Jul-05 5:34 
GeneralIncorrect Singleton Pattern implementation Pin
eanderson13-Jul-05 4:33
eanderson13-Jul-05 4:33 
Your last code snippet, the one for the singleton pattern is wrong. If the object is not yet instantiated, it will get instantiated but not returned.

The correct code would be:

Public Class WinApplicationContext
Implements IApplicationContext
Shared _MyInstance As IApplicationContext

Private Sub New() 'Private Constructor
End Sub Public

Shared Function ApplicationContext() As IApplicationContext
' Shared Property to return the instance.
If _MyInstance Is Nothing Then
_MyInstance = New WinApplicationContext
End
Return _MyInstance

End Function
End Class
GeneralRe: Incorrect Singleton Pattern implementation Pin
SATHISH CG13-Jul-05 18:31
SATHISH CG13-Jul-05 18:31 
Questionwhat is this article about? Pin
stax7612-Jul-05 23:13
stax7612-Jul-05 23:13 
AnswerRe: what is this article about? Pin
SATHISH CG13-Jul-05 18:30
SATHISH CG13-Jul-05 18:30 
GeneralRe: what is this article about? Pin
Anonymous13-Jul-05 22:49
Anonymous13-Jul-05 22:49 
GeneralRe: what is this article about? Pin
PTJA19-Jul-05 23:42
PTJA19-Jul-05 23:42 
GeneralErrorProvider Pin
WillemM12-Jul-05 6:39
WillemM12-Jul-05 6:39 
GeneralSuggestion to make this more convincing Pin
gxdata4-Jul-05 19:10
gxdata4-Jul-05 19:10 

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

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