Click here to Skip to main content
15,881,281 members
Articles / Web Development / ASP.NET

Building a Better ASP.NET 1.1 BasePage Framework

Rate me:
Please Sign up or sign in to vote.
4.52/5 (27 votes)
5 Dec 2005CPOL35 min read 124.9K   2.1K   156  
This is a journey on how to build a better Base page. The result of this will be a reusable framework that you can use to create as many Base pages as you like (on many different sites) and still have something that will keep the designers on your team happy.
using System;
using Web = System.Web;
using WebUI = System.Web.UI;
using WebCtl = System.Web.UI.WebControls;
using HtmlCtl = System.Web.UI.HtmlControls;
using BasePageFramework.SmartState;
using BasePageFramework.Configuration;


namespace BasePageFramework
{
	/// <summary>
	/// Summary description for basePage.
	/// </summary>
	public class SuperBasePage : WebUI.Page
	{
		
		#region Constants
		
		private const int LG_STR_BUFFER_SZ = 512;  //large string builder initial buffer size
		private const int MD_STR_BUFFER_SZ = 256;  //medium string builder inital buffer size
		private const int SM_STR_BUFFER_SZ = 128;  //small (default) string builder buffer size
		
		private const string OPEN_TITLE_TAG = "<TITLE>";
		private const string CLOSE_TITLE_TAG = "</TITLE>";
		private const string OPEN_HEAD_TAG = "<HEAD>";
		private const string CLOSE_HEAD_TAG = "</HEAD>";
		private const string OPEN_HTML_TAG = "<HTML>";
		private const int DEFAULT_LINK_WIDTH = 20;  //this is used to estimage the number of characters to reserve space for in our string builders in an attempt to maximize efficency
		
		#endregion
		
		#region SuperBasePage Members
		
		private WebCtl.PlaceHolder _serverFormPlcHldr = new WebCtl.PlaceHolder(); //our form sits in this placeholder
		private string _title = string.Empty; //the title of the page (displayed in the caption bar)
		private BaseServerForm _baseForm = null; //new SuperBasePage("BASE_FORM"); //the form tag where we place all our webserver controls
		private Configuration.BasePageConfiguration _config = null;
		private string _basePageName = string.Empty;
		private ISmartQueryString _smartQueryString = null;
		private ISmartSession _smartSession = null;
		private ISmartApplication _smartApp = null;

		#endregion
		
		#region Properties
		
		public string Title
		{
			get{return this._title;}
			set{this._title = value;}
		}
		
		public BaseServerForm BaseForm 
		{ 
			get{return this._baseForm;}
		 
		}

		public IBaseTemplate MainUITemplate 
		{ 
			get
			{
				return this._baseForm.BaseTemplate;
			}
		}

		public ISmartSession SmartSession
		{
			get
			{
				if(this._smartSession == null)
					throw new InvalidOperationException("You must initialize the smart session state before you can reference it");
				else
					return this._smartSession;
			}
		}

		public ISmartApplication SmartApplication
		{
			get
			{
				if(this._smartApp == null)
					throw new InvalidOperationException("You must initialize the smart application state before you can reference it");
				else
					return this._smartApp;
			}
		}

		public ISmartQueryString SmartQueryString
		{
			get
			{
				if(this._smartQueryString == null)
					throw new InvalidOperationException("You must initialize the smart query string before you can reference it");
				else
					return this._smartQueryString;
			}
		}
		#endregion
		
		protected override void CreateChildControls()
		{
			InitSmartQueryString(ref this._smartQueryString);
			
			if(this._smartQueryString != null)
				this._smartQueryString.SetQueryStringInfo(this.Page);

			InitSmartSession(ref this._smartSession);
			
			if(this._smartSession != null)
				this._smartSession.SetSessionState(this.Page.Session);

			BasePreRender(this._baseForm);     //allows us to set the base page state from the child page before any rendering of the base page occurs
			LoadTemplatePanels();  //call the virutal method to allow the base page to set the appropriate usercontrols into the base template
		}

		protected override void OnInit(EventArgs e)
		{
			this.EnsureChildControls();
			base.OnInit (e);
		}

		protected override void AddParsedSubObject(Object obj)
		{
			//put all the configuration extras into our html header here
			//make sure you modify the body tag as well since it is also 
			//editable from our custom configuration section
			if(obj is HtmlCtl.HtmlForm)
			{
				BasePageFramework.IBaseTemplate tmplt = null;
				this.LoadBaseUITemplate(ref tmplt);
				if(tmplt == null)
					throw new InvalidOperationException("Unable to load the base UI Template");
				
				this._baseForm = new BaseServerForm((HtmlCtl.HtmlForm)obj,tmplt);
				
				obj = this._baseForm; //replace the object reference with our own "base server form"
			}
			if(obj is Web.UI.LiteralControl)
			{
				Web.UI.LiteralControl htmlHeader = (Web.UI.LiteralControl)obj;
				
				//we only need to do this processing when we are in the header
				if(htmlHeader.Text.ToUpper().IndexOf(OPEN_HTML_TAG)>=0)
				{
					//we are going to need the stuff from the configuration now so load it up...
					ConfigLoadingArgs cfgParam = new ConfigLoadingArgs();
					InitializeConfiguration(ref cfgParam);
					this._config = (Configuration.BasePageConfiguration)Cache["BASE_CONFIGURATION_" + cfgParam.BasePageID];
					if(this._config == null)
						InitFromConfiguration(cfgParam.BasePageID);
				
					if(this._config != null) //we will only do this after the configuration is initialized otherwise we can just use the default html header
					{
						System.Text.StringBuilder htmlBuilder = new System.Text.StringBuilder(htmlHeader.Text.Length*2);
						htmlBuilder.Append(htmlHeader.Text);

						//check to see if the current literal control being passed to us is actually 
						//the HTML header - if it is then we need to add any values from the custom web.config settings
						//if the current page has any of the same values in the body tag or any inline scripts -
						//then we need to allow the page to override the web.config settings - this is in following with the
						//same pattern for everything in ASP.NET (machine.config-web.config-page...)
					
						//see if the title is filled in if not insert the default title <title></title>
						//if the title tag is missing then add one now
						int openingTitleTagOffset =  htmlHeader.Text.ToUpper().IndexOf(OPEN_TITLE_TAG,0,htmlHeader.Text.Length);
						int endTitleTagOffset = htmlHeader.Text.ToUpper().IndexOf(CLOSE_TITLE_TAG,0,htmlHeader.Text.Length);
						endTitleTagOffset -= CLOSE_TITLE_TAG.Length-1;

						int titleLen = htmlHeader.Text.Substring(openingTitleTagOffset, endTitleTagOffset - openingTitleTagOffset).Length; //get the length of our title
					
						if(openingTitleTagOffset >= 0)
						{
							//check the length of our title
							//if the title is missing then add the default one
							if(titleLen == 0)
								htmlBuilder.Insert(openingTitleTagOffset+OPEN_TITLE_TAG.Length,this._config.DefaultTitle);
						}
						else
						{
							int openHeadTagOffset = htmlHeader.Text.ToUpper().IndexOf(OPEN_HEAD_TAG,0,htmlHeader.Text.Length);
							//if the head tag is missing then add it otherwise just insert the default title if needed
							//create another string Builder to handle our missing header/title tag
							System.Text.StringBuilder subHtml = 
								new System.Text.StringBuilder(
								(OPEN_HEAD_TAG.Length 
								+ CLOSE_HEAD_TAG.Length 
								+ OPEN_TITLE_TAG.Length 
								+ CLOSE_TITLE_TAG.Length)
								+_config.DefaultTitle.Length);

							if(openHeadTagOffset == -1)
							{
								subHtml.Append(OPEN_HEAD_TAG);
								subHtml.Append(OPEN_TITLE_TAG);
								subHtml.Append(_config.DefaultTitle);
								subHtml.Append(CLOSE_TITLE_TAG);
								subHtml.Append(CLOSE_HEAD_TAG);
								//insert the output right after the opening HTML tag
								htmlBuilder.Insert(OPEN_HTML_TAG.Length,subHtml.ToString());
							}
							else
							{
								subHtml.Append(OPEN_TITLE_TAG);
								subHtml.Append(_config.DefaultTitle);
								subHtml.Append(CLOSE_TITLE_TAG);
								//insert the output right after the opening Head Tag
								htmlBuilder.Insert(openHeadTagOffset,subHtml.ToString());
							}
						}	

						//insert our configuration metatags and scripts/links after the closing title tag...
						//int closingTitleTagOffset = htmlBuilder.ToString().ToUpper().IndexOf(CLOSE_HEAD_TAG,0,htmlBuilder.ToString().Length);
						htmlBuilder.Replace(CLOSE_HEAD_TAG,GetWebConfigLinks(htmlBuilder.ToString()));
						RenderBodyTagLinks(htmlBuilder);
						//replace the text in the literal control with our new header
						htmlHeader.Text = htmlBuilder.ToString();
					}
				}
			}
			this.Controls.Add((System.Web.UI.Control)obj);
		}
		
		
		#region HTML Header Parsing Code

		private void RenderBodyTagLinks(System.Text.StringBuilder htmlBuilder)
		{
			//rip out the body tag and replace it with our own
			//that has the attributes from the web config file
			//but allows the individual pages to override these settings at the page level
			string bodyTag = htmlBuilder.ToString();
			int bodyTagOffset = bodyTag.ToUpper().IndexOf("<BODY");
			bodyTag = bodyTag.Substring(bodyTagOffset,bodyTag.Length - bodyTagOffset);
			
			//create the attributes that will be dumped into the body tag
			//make sure it is not already in the body before entering it
			//if it is in the body then throw it out so the individual page
			//can override the web.config's settings
	
			System.Text.StringBuilder bodyTagBuilder = new System.Text.StringBuilder(SM_STR_BUFFER_SZ);
			bodyTagBuilder.Append("<BODY ");

			foreach(string attribute in _config.BodyAttributes.Keys)
				if(bodyTag.IndexOf(attribute) == -1)
				{
					bodyTagBuilder.Append(" ");
					bodyTagBuilder.Append(attribute);
					bodyTagBuilder.Append("=\"");
					bodyTagBuilder.Append(_config.BodyAttributes.Get(attribute));
					bodyTagBuilder.Append("\" ");
				}
			
			//append the global body tag attributes to the end of the current attribute list in our body tag
			string newAttribList = bodyTag.Substring(5,bodyTag.Length-6); //remove the opening body tag and closing bracket
			
			//get the current attributes out of the old body tag and add them to our bodytagbuilder
			bodyTagBuilder.Append(" ");
			bodyTagBuilder.Append(newAttribList);
			
			int bodytagoffset = htmlBuilder.ToString().IndexOf(bodyTag);

			//remove the old body tag from our string builder
			htmlBuilder.Remove(bodytagoffset,htmlBuilder.Length-bodytagoffset);
			string output = htmlBuilder.ToString();

			htmlBuilder.Append(bodyTagBuilder.ToString());
		}

		//takes the existing script links and returns a new string
		//containing the existing links merged with the ones in the web.config
		private string GetWebConfigLinks(string oldHtml)
		{
			System.Text.StringBuilder subHtml = new System.Text.StringBuilder((_config.Scripts.Count*DEFAULT_LINK_WIDTH)+CLOSE_HEAD_TAG.Length);
			this.GetMetaTags(subHtml,oldHtml);
			this.GetStyleSheetLinks(subHtml);
			this.GetScriptLinks(subHtml);
			subHtml.Append("\r\n\t");
			subHtml.Append(CLOSE_HEAD_TAG);
			return subHtml.ToString();
		}

		private void GetMetaTags(System.Text.StringBuilder htmlBuilder,string oldHtml)
		{
			foreach(string metaTag in _config.MetaTags.Keys)
			{
				if(oldHtml.IndexOf(metaTag) == -1)
				{
					htmlBuilder.Append("\r\n\t\t");
					htmlBuilder.Append("<META ");
					htmlBuilder.Append("NAME=\"");
					htmlBuilder.Append(metaTag);
					htmlBuilder.Append("\"");
					htmlBuilder.Append(" CONTENT=\"");
					htmlBuilder.Append(_config.MetaTags.Get(metaTag));
					htmlBuilder.Append("\"/>");
				}
			}
		}
		
		private void GetStyleSheetLinks(System.Text.StringBuilder scriptLinks)
		{
			foreach(string key in this._config.StyleSheets.Keys)
			{
				scriptLinks.Append("\r\n\t\t<LINK ");
				scriptLinks.Append("type=\"text/css\" ");
				scriptLinks.Append("rel=\"stylesheet\" ");
				scriptLinks.Append("href=\"");
				scriptLinks.Append(ParseRootPath(this._config.StyleSheets.Get(key)));
				scriptLinks.Append("/");
				scriptLinks.Append(key);
				scriptLinks.Append("\"");
				scriptLinks.Append("/>");
				scriptLinks.Append(Environment.NewLine);
			}
		}
		
		private void GetScriptLinks(System.Text.StringBuilder scriptLinks)
		{
			foreach(string key in this._config.Scripts.Keys)
			{
				scriptLinks.Append("\r\n\t\t<SCRIPT lanugage=\"javascript\" src=\"");
				scriptLinks.Append(ParseRootPath(this._config.Scripts.Get(key)));
				scriptLinks.Append("/");
				scriptLinks.Append(key);
				scriptLinks.Append("\">");
				scriptLinks.Append("</SCRIPT>");
			}
		}
		
		#endregion

		#region Utility Methods

		private string ParseRootPath(string path)
		{
			if(path.IndexOf('~') == 0)
			{
				if(System.Web.HttpContext.Current.Request.ApplicationPath == "/")
				{
					path = path.Replace("~","");
				}
				else
					path = path.Replace("~",System.Web.HttpContext.Current.Request.ApplicationPath);
			
				return path;
			}
			else
				return path;
		}		

		private void InitFromConfiguration(string pageName)
		{
			this._config = (Configuration.BasePageConfiguration)Cache["BASE_CONFIGURATION_" + pageName];
			if(this._config == null)			
			{
				Configuration.BasePagesConfiguration baseConfiguration = (Configuration.BasePagesConfiguration)System.Configuration.ConfigurationSettings.GetConfig("base.configuration");
				if(baseConfiguration == null)
					return; //the base configuration was not set in the current web.config - exit now...
				
				 //load the current page's configuration and stick it into cache so it does not have to be reloaded on subsequent requests
				this._config = baseConfiguration[pageName];
				Cache.Add("BASE_CONFIGURATION_" + pageName,this._config,null,DateTime.MaxValue,TimeSpan.FromDays(100),System.Web.Caching.CacheItemPriority.NotRemovable,null);
			}
		}
		
		
		#endregion

		#region Virutal Methods

		protected virtual void InitializeConfiguration(ref ConfigLoadingArgs configParams)
		{}

		/// <summary>
		/// override this method in our child pages to allow us to set base page state before any rendering takes place
		/// in the base page
		/// </summary>
		protected virtual void BasePreRender(BaseServerForm baseForm)
		{}

		/// <summary>
		/// override this method to allow the base page to load the base UI template
		/// the base UI template is of type BaseUITemplate BaseUITemplate is of type Usercontrol
		/// </summary>
		protected virtual void LoadBaseUITemplate(ref BasePageFramework.IBaseTemplate tmplt)
		{}
		
		/// <summary>
		/// override in the base page so you can load user controls at the derived base page
		/// where you want them to be loaded - this is done so the derived base page can subscribe to events
		/// fired by these controls
		/// </summary>
		protected virtual void LoadTemplatePanels()
		{}
		
		/// <summary>
		/// override this method to initialize the smart querystring with your derived smart querystring instance 
		/// </summary>
		/// <param name="sqs">derived instance of a smart query string base class that Implements the ISmartQueryString interface</param>
		protected virtual void InitSmartQueryString(ref ISmartQueryString isqs)
		{}
		
		/// <summary>
		/// override this method to initialized the smart session state with your derived smart session instance
		/// </summary>
		/// <param name="ises">derirved smart session object that implements the ISmartSession Interface</param>
		protected virtual void InitSmartSession(ref ISmartSession ises)
		{}
		
		/// <summary>
		/// override this method to initialize the smart application state with your derived smart application instance
		/// </summary>
		/// <param name="iapp">derived smart application object that implements the ISmartApplication interface</param>
		protected virtual void InitSmartApplication(ref ISmartApplication iapp)
		{}

		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer (Senior) Priority Courier Experts
United States United States
software developer for about 25 years now.

Comments and Discussions