Click here to Skip to main content
Click here to Skip to main content

Master Pages and Factory Pattern

, 17 Jan 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
Master pages and factory patterns using ASP.NET

Introduction

Here I am explaining how one can take the full advantage of master pages. Visit http://www.odetocode.com/Articles/450.aspx to get the excellent knowledge of master page. There are plenty of articles explaining insights of Master Pages this is certainly not the one. Sometime you stuck in a situation where you have same GUI, like the one shown in figure, but different business scenario, you start thinking about user controls to maintain standardized look across all pages. What if I say using Master pages with Factory pattern makes you feel in heaven? Let's see how. I have used the clickable grid which was submitted by code Project user BlaiseBraye.

Background

Read about master pages before reading this article. It will help you understand this article.

Application Flow

When user selects order detail from the menu, parent grid will be populated with order list and child grid will be populated with the list of products included in that order, when user selects employee territory parent grid will be populated with list of employees and child grid will be populated with the list of territories for the selected employee. Also there are two content pages which show related info of the selected item.

Using the code

The download code contains dbcontroller1 helper class. You need to change the connection string there to connect to your SQL server. Backend database is Northwind which is shipped with SQL server.

Overview

This application has a master page which consists of a menu control and two Grids parent and child. When I say parent and child it means child grid is dependent on parent grid for its source.

Let's take an example of Order and order detail tables in Northwind Database. We have a list of orders populated in one grid by selecting any of the order in parent grid child grid is populated with the list of products for that order. This scenario also applies for employee and employee territory case. You could have a lot of similar scenario in your application. What will you do? Create separate similar pages or create a user control. Though user control is a good choice but let's go with master pages.

Before going into the detail of the application let's explore what Factory pattern is?

Factory Method

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses (GOF). There is one more variation of Factory method which is called Parameterized factory Method, which states The factory method takes a parameter that identifies the kind of object to create. All objects the factory method creates will share the Product interface. This is what we are going for. Show below is the graphical representation of Parameterized Factory method. Factory methods eliminate the need to bind application-specific classes into your code. The code only deals with the Base interface; therefore it can work with any user-defined classes which have the same interface as Base.

Implementing Factory Method

We have an abstract class Base which contains four abstract methods. Order and Employee class will inherit from our abstract class Base. Below is the code for our Base abstract class. BindParentGrid will bind the parent grid with the parent source which could be Order list or Employee list guided by user selection. BindChildGrid method will bind the child grid to the child source i.e. Products or Employee Territory. getParentData will return the info related to the Parent source and getChildData will return the info related to the child source, Key is the parameter passed to the method by Parent Grid.

///<span class="code-SummaryComment"><summary>
</span>
/// This is the base abstract class. all business classes will inherit 
/// from this class
/// This is used for factory pattern.
///<span class="code-SummaryComment"></summary>
</span>
      public abstract class Base
      { 
        public abstract void BindParentGrid(
           DataDigest.WebControls.GridView gvParent);
        public abstract void BindChildGrid(
           DataDigest.WebControls.GridView 
           gvChild, string ParentId);
        public abstract DataSet getParentData();
        public abstract DataSet getChildData(string Key);
    } 

We derive two classes from our abstract class which controls the Grid binding i.e. BIOrderMaster and BIEmployeeMaster.Below is the code of one of our inherited class. We are eliminating the discussion of our Data Access components, you can see the details in attached source code.

///<span class="code-SummaryComment"><summary>
</span>
/// This will control the opertaion when user selects 
/// OrderDetail from the menu
///<span class="code-SummaryComment"></summary>
</span>
      public class BIOrderMaster : Base
      {
         private DAccessOrder Orders = newDAccessOrder();
         private DataSet dsParent = newDataSet();
         public BIOrderMaster() 
         {
            dsParent = Orders.getOrders();
         }
         public override void BindParentGrid(DataDigest
            .WebControls.GridView gvParent)
         {
            if (dsParent == null) {
               dsParent = Orders.getOrders();
            }
            string[] key = newstring[1];
            key[0] = "OrderID";
            gvParent.DataSource = null;
            gvParent.Columns.Clear();
            gvParent.DataSource = dsParent;
            gvParent.DataKeyNames = key;
            BoundField TtlCoNm = newBoundField();
            TtlCoNm.DataField = "OrderID";
            TtlCoNm.HeaderText = "Order ID";
            gvParent.Columns.Add(TtlCoNm);
            TtlCoNm.ItemStyle.Width = 600;
            gvParent.DataBind();
        }
        public override void BindChildGrid(DataDigest
           .WebControls.GridView gvChild, 
           string ParentId)
        {
           string[] key = newstring[1];
           key[0] = "ProductID";
           DataSet dsChild = Orders.getProducts(ParentId);
           gvChild.SelectedIndex = -1;
           gvChild.DataSource = null;
           gvChild.Columns.Clear();
           gvChild.DataSource = dsChild;
           gvChild.DataKeyNames = key;
           BoundField BrName = newBoundField();
           BrName.DataField = "ProductName";
           BrName.HeaderText = "Product Name";
           gvChild.Columns.Add(BrName);
           BrName.ItemStyle.Width = 600;
           gvChild.DataBind();
        }
        public override DataSet getParentData()
        {
           return dsParent;
        }
        public override DataSet getChildData(string Key)
        {
           return Orders.getProducts(Key);
        }
   }
   

Whenever user selects a particular item from menu we store page identity in session. Master Page passes page identity to our factory class which returns appropriate object of Base type. A type is a name used to denote a particular interface. We speak of an object as having the type "Window" if it accepts all requests for the operations defined in the interface named "Window."(GOF) Our derived classes BIOrderMaster and BIEmployeeMaster have the same type as they both shared the Base interface. Our factory class is shown below:

Factory Class

///<span class="code-SummaryComment"><summary>
</span>
/// Return object of type Base depending on the parameter 
/// provided to its getobject method
///<span class="code-SummaryComment"></summary>
</span>
public class Factory
{          
    public Factory()
    {
    }
    
    publicBase GetObject(string type)
    {
        Base objbase = null;
        switch (type)
        {
            case "Order Detail":
                objbase = newBIOrderMaster();
                break;
            case "Employee Territorie":
                objbase = newBIEmployeeMaster();
                break;
            default:
                throw new Exception("Unknown Object");
        }
        return objbase;
    }
}

Master Page

let's see what happens when user request Order detail page which is our one of two content pages. Order of events is important when we are working with master pages. Page load event for the page is fire before the page load event of the master page but page init event for the master page is fired before page init event of page. This is important and we will discuss it later in this article. For now we only need to know the order of events which are as follows:

  • Master – initialize.
  • Content- initialize.
  • Content- load.
  • Master – load.
  • Content- render.
  • Master- render.

Let's have a close look on master page code behind event by event

First we have create an object of our factory class and declared an object of type Base. You guys have also noticed that we have couple of events in our master page class. These events are used to notify our content page that grid selection in our master page is changed and they must take appropriate action to counter that change. I am not going in details of custom events and delegate as there are plenty of material available about delegates. (Its handy to know that delegates are the example of observer pattern). We also have couple of public properties.

private Factory Factory = newFactory();
private Base currentObject;
    
// to raise the event in content page whenever 
// grid selection changes
public event GridSelectionChanged GridChanged;
public event SecondryGridSelectionChanged ChildGridChanged;
 
#region Properties
//Public properties exposed by the master page for 
//content pages 
public string mainTitle
{
  set
  {
    pageTitle.Text = value;
  }
}
publicBase myObject
{
    get
    {
        return currentObject;
    }
}
#endregion

At the page init event we are requesting our factory for the appropriate Base type object. Property myObject returns this newly created object. This property is used by content pages to get the current business object at their page load event. Remember we talked about the order of events. We call our factory class getObject method in the page_init event if we replace the code to page load event we wont be able to get its reference from our content page because Page_load event for the content page occurs before the page_load event of master page.

protected void Page_Init(object sender, System.EventArgs e)
{
    if (Session["PageName"] != null)
    {
        if (Session["PageName"].ToString() != 
            "Home")
        {
            currentObject = Factory.GetObject(Session[
                "PageName"].ToString());
        }
    }
}

At the page load event of master page we are requesting currentObject which is our Base type object to bind our parent grid. Similarly on parent grid SelectedIndexChanged event we are requesting currentObject to bind our child grid. In GV_TitleCompany_SelectedIndexChanged we are also notifying our content page that a grid selection has changed. We have extended eventArg class to hold the information about the event, Datakey of the grid in this case.

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        if (Session["PageName"] != null)
        {
            if (Session["PageName"].ToString() 
                != "Home")
            {
                currentObject.BindParentGrid(
                              GV_TitleCompany);
            }
            else
            {
                myPanel.Visible = false;
                Panel1.Visible = false;
                ClearGrid();
            }
        }
        else
        {
            myPanel.Visible = false;
            Panel1.Visible = false;
            ClearGrid();
        }
    }
}
 
protected void GV_TitleCompany_SelectedIndexChanged(
                          object sender, EventArgs e)
{
    GridViewRow row = GV_TitleCompany.SelectedRow;
    currentObject.BindChildGrid(GV_BranchName, 
       GV_TitleCompany.DataKeys[GV_TitleCompany
       .SelectedIndex][0].ToString().Trim());
    MasterGridEventArgument eventArgs = new 
       MasterGridEventArgument(GV_TitleCompany.DataKeys[
       GV_TitleCompany.SelectedIndex].Value.ToString()
       .Trim());
    if (GridChanged != null)
    {
        GridChanged(this, eventArgs);
    }
}

Now let's move on to one of our content page.

Order Page

public partial class Pages_Default : System.Web.UI.Page
{
    privateBase Orders;
 
    protectedvoid Page_Load(object sender, EventArgs e)
    {
        Pages_MasterGrid myMaster = (Pages_MasterGrid)
           this.Master;
        myMaster.mainTitle = "Order Information";
        Orders = myMaster.myObject;
    }
 
    protected void Page_Init(object sender, 
                             System.EventArgs e)
    {
        Pages_MasterGrid myMaster = (Pages_MasterGrid)
           this.Master;
        myMaster.GridChanged +=  FillControls;
        myMaster.ChildGridChanged += FillChildInfo;
    }
 
    protected void FillControls(object sender, 
                    MasterGridEventArgument e)
    {
         //Code to fill the controls with parent info
    }
 
    protected void FillChildInfo (object sender, 
                      DetailGridEventArgument e)
    {
        //Code to fill the controls with child info
    }
 
}

At the page_init event we are adding handlers for our custom events which will be raised by Parent and Child grid in our Master page then at page load event we are setting Page title through Public property exposed by our Master page and also we are getting the currentObject from our Master page. We attached FillControl function to our GridChanged event and FillChildInfo to our ChildGridChanged event. These functions will populate our controls contained in Multiview control. Complete code is present in attached source code.

Final say

That's all; we have implemented mater pages with Factory Pattern. You guys are free to made any changes and give healthy comments on this articles. Cheers.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Saud AKhter
Software Developer (Senior) Systems Limited
Pakistan Pakistan
I have graduated from SirSyed university of engineering and technology in year 2005. Working on Microsoft Platform for 3 years.
I like to work on new technologies. Always keen to learn new orientation in software design. Design patterns is the area for which i am looking to work on.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberSaud AKhter6-Aug-12 21:34 
General[My vote of 1] Really Bad Article PinmemberRitesh Totlani26-Sep-09 6:08 
Generalunable to run the code PinmemberMember #247484223-Jan-07 0:39 
GeneralRe: unable to run the code Pinmemberssaud23-Jan-07 1:10 
Questionnice Article PinmemberUmer Khan18-Jan-07 1:08 
AnswerRe: nice Article Pinmemberssaud18-Jan-07 1:18 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411023.1 | Last Updated 18 Jan 2007
Article Copyright 2007 by Saud AKhter
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid