Click here to Skip to main content
15,891,136 members
Articles / Web Development / HTML

Site Compass - An ASP .NET implementation of cookie crumb navigation

Rate me:
Please Sign up or sign in to vote.
4.77/5 (18 votes)
13 Nov 200411 min read 73.9K   1.5K   66  
Uses ASP .NET sessions to track and display a users navigation through your site
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Collections;

namespace IlluminatedIndustries.WebCustomControls
{
	/// <summary>
	/// Site Compass Classes - The class generates and maintains a ASP .NET cookie crumb system
	/// </summary>
	[
	Designer("IlluminatedIndustries.WebCustomControls.SiteCompassDesigner, SiteCompass"),
	DefaultProperty("DisplayTitle"),
	ToolboxData("<{0}:SiteCompass runat=server></{0}:SiteCompass>")
	]
	public class SiteCompass : System.Web.UI.WebControls.WebControl
	{
		#region Class Variable Declarations
		public enum CompassStyles
		{
			VerticalHTML,
			HorizonatalHTML,
			DHTMLMenu
		}
		private CompassStyles _compassStyle = CompassStyles.HorizonatalHTML;
		public enum ItemLinkTargets
		{
			_self,
			_top,
			_blank,
			_parent
		}
		private ItemLinkTargets _itemLinkTarget = ItemLinkTargets._self;
		public enum ItemLinkOverrideTargets
		{
			none,
			_self,
			_top,
			_blank,
			_parent
		}
		private ItemLinkOverrideTargets _overrideTarget = ItemLinkOverrideTargets.none;
		public enum Modes
		{
			Normal,
			DisplayOnly
		}
		private Modes _mode = Modes.Normal;

		private bool _caseSensitiveUrlComparison = false;
		private bool _linkLastItem = false;
		private bool _suppressIcons = false;
		
		private int _maxItems = 8;

		private string _cssClassLink = string.Empty;
		private string _currentPageIndicator = string.Empty;
		private string _displayTitle = string.Empty;
		private string _divName = string.Empty;
		private string _itemIconAlt = string.Empty;
		private string _itemSeperator = " >> ";
		private string _itemIconPath = string.Empty;
		private string _sessionKey = "EE517D2A-DE98-42b4-B64F-DF8967BF210E";
		private string _prefixCode = string.Empty;
		private string _postfixCode = string.Empty;
		#endregion

		#region Appearance Property Accessors
		[
		Bindable(true),
		Category("Appearance"),
		DefaultValue(CompassStyles.HorizonatalHTML),
		Description("The style of compass to use.  If you choose DHTML menu ensure you deploy (and reference it within your code) the siteCompassDHTML.js dependency file with your webproject."),
		]
		public CompassStyles CompassStyle
		{
			get	{ return this._compassStyle; }
			set	{ this._compassStyle = value; }
		}

		[
		Bindable(true),
		Category("Appearance"),
		DefaultValue(" >> "),
		Description("The seperator between Site Compass items (ensure that you assign spaces if required).")
		]
		public string ItemSeperator
		{
			get	{ return this._itemSeperator; }
			set	{ this._itemSeperator = value; }
		}

		[
		Bindable(true),
		Category("Appearance"),
		DefaultValue(""),
		Description(@"Any HTML code entered here will be rendered
		within the compass DIV before the compass item code (within 'a'
		tags) when rendering under vertical or horizontal html. In
		DHTML mode use this field to enter a menu title.")
		]
		public string PrefixCode
		{
			get { return this._prefixCode; }
			set { this._prefixCode = value; }
		}

		[
		Bindable(true),
		Category("Appearance"),
		DefaultValue(""),
		Description("Any HTML code entered here will be rendered within the compass DIV after the compass code (within <a> tags)")
		]
		public string PostfixCode
		{
			get { return this._postfixCode; }
			set { this._postfixCode = value; }
		}
		
		[
		Bindable(true),
		Category("Appearance"),
		DefaultValue(""),
		Description("This is the class name to use for the links in the compass, this will allow you to set specific hover etc css properties")
		]
		public string CssClassLink
		{
			get { return this._cssClassLink; }
			set { this._cssClassLink = value; }
		}
		
		[
		Bindable(true),
		Category("Appearance"),
		DefaultValue(""),
		Description("Set this value to show it agaisnt the current page.")
		]
		public string CurrentPageIndicator
		{
			get { return this._currentPageIndicator; }
			set { this._currentPageIndicator = value; }
		}
		#endregion

		#region Compass Item Property Accessors
		[
		Bindable(true),
		Category("Compass Item"),
		DefaultValue(""),
		Description("The text used on the Site Compass item to identify the current page.")
		]
		public string DisplayTitle
		{
			get	{ return this._displayTitle;	}
			set	{ this._displayTitle = value; }
		}
		[
		Bindable(true),
		Category("Compass Item"),
		DefaultValue(""),
		Description("This is the \"alt\" value that will be assigned to your item icon.")
		]
		public string IconAlt
		{
			get { return this._itemIconAlt; }
			set { this._itemIconAlt = value; }
		}

		[
		Bindable(true),
		Category("Compass Item"),
		DefaultValue(""),
		Description("Path to the icon to use for this pages compass item, if not set no image will be displayed.  This path must be relative to the server root.")
		]
		public string IconPath
		{
			get { return this._itemIconPath; }
			set	{ this._itemIconPath = value; }
		}
		[
		Bindable(true),
		Category("Compass Item"),
		DefaultValue(ItemLinkTargets._self),
		Description("The target attribute of the compass item link.")
		]
		public ItemLinkTargets LinkTarget
		{
			get { return this._itemLinkTarget; }
			set { this._itemLinkTarget = value; }
		}
		#endregion

		#region Behavior Property Accessors
		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(8),
		Description("The maximum number of items to display in the Site Compass control. Set to 0 to display all items in the current compass")
		]
		public int MaxItems
		{
			get	{ return this._maxItems;	}
			set	{ this._maxItems = value; }
		}

		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(false),
		Description("Indicates if the last item in the Site Compass is linked.")
		]
		public bool LinkLastItem
		{
			get { return this._linkLastItem; }
			set	{ this._linkLastItem = value; }
		}

		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(false),
		Description("If set to true cases will be ignored when comparing compass items.")
		]
		public bool CaseSensitiveUrlComparison
		{
			get { return this._caseSensitiveUrlComparison; }
			set	{ this._caseSensitiveUrlComparison = value; }
		}

		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(Modes.Normal),
		Description("Set to \"display only\" mode to render the current items in the compass without adding to it.")
		]
		public Modes Mode
		{
			get { return this._mode; }
			set { this._mode = value; }
		}
		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(false),
		Description("Set this value to true if you want to prevent item icons from being displayed even if an item has one allocated.")
		]
		public bool SuppressIcons
		{
			get { return this._suppressIcons; }
			set { this._suppressIcons = value; }
		}

		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(""),
		Description("This value is assigned as the \"name\" attribute of the DIV containing the compass.  This is useful if you want to refer to the DIV in your own javascripts.")
		]
		public string DivName
		{
			get { return this._divName; }
			set { this._divName = value; }
		}

		[
		Bindable(true),
		Category("Behavior"),
		DefaultValue(ItemLinkOverrideTargets.none),
		Description("Set this value to override any target attribute currently set in compass items (this does not change the item itself).")
		]
		public ItemLinkOverrideTargets OverrideTarget
		{
			get { return this._overrideTarget; }
			set { this._overrideTarget = value; }
		}
		#endregion

		#region Misc Property Accessors
		[
		Bindable(true),
		Category("Misc"),
		DefaultValue("EE517D2A-DE98-42b4-B64F-DF8967BF210E"),
		Description("A unique key to identify the compass data in the current session.")
		]
		public string SessionKey
		{
			get { return this._sessionKey; }
			set	{ this._sessionKey = value; }
		}
		#endregion

		#region Disabled Property Accessors
		//These properties are overridden to prevent them
		//from being displayed in the designer properties
		//are they are not relevant to the control but come
		//as default through the inheritance from
		//System.Web.UI.WebControls.WebControl
		//
		//I wasnt entirely happy with the way the HtmlTextWriter
		//class creates HTML when using these properties so I
		//decided to force the use of stylesheets
		[Browsable(false)]
		public override FontInfo Font
		{
			get { return base.Font;	}
		}

		[Browsable(false)]
		public override System.Drawing.Color ForeColor
		{
			get	{ return base.ForeColor; }
			set	{ base.ForeColor = value; }
		}

		[Browsable(false)]
		public override System.Drawing.Color BackColor
		{
			get { return base.BackColor; }
			set	{ base.BackColor = value; }
		}

		[Browsable(false)]
		public override System.Drawing.Color BorderColor
		{
			get	{ return base.BorderColor; }
			set	{ base.BorderColor = value;	}
		}

		[Browsable(false)]
		public override BorderStyle BorderStyle
		{
			get	{ return base.BorderStyle; }
			set	{ base.BorderStyle = value;	}
		}

		[Browsable(false)]
		public override Unit BorderWidth
		{
			get { return base.BorderWidth; }
			set	{ base.BorderWidth = value;	}
		}

		[Browsable(false)]
		public override Unit Height
		{
			get	{ return base.Height; }
			set	{ base.Height = value; }
		}

		[Browsable(false)]
		public override Unit Width
		{
			get	{ return base.Width; }
			set	{ base.Width = value; }
		}
		#endregion

		#region Generic Overrides
		/// <summary>
		/// Render this control to the output parameter specified.
		/// </summary>
		/// <param name="output"> The HTML writer to write out to </param>
		protected override void Render(HtmlTextWriter output)
		{
			BuildCompassCache();
			
			switch (this.CompassStyle)
			{
				case CompassStyles.HorizonatalHTML:
				case CompassStyles.VerticalHTML:
					GenerateCompass_HTML(output, false);
					break;
				case CompassStyles.DHTMLMenu:
					GenerateCompass_DHTMLMenu(output, false);
					break;
			}
		}
		#endregion

		#region Implementation
		/// <summary>
		/// Builds the Site Compass item cache
		/// </summary>
		private void BuildCompassCache()
		{
			//If the control is in DisplayOnly mode there
			//is no need to adjust or change the session
			//data so simply return out
			if (this.Mode == Modes.DisplayOnly)
			{
				return;
			}

			//declare an arraylist to hold the compass data
			ArrayList compassData;

			//if the session data is null then instantiate
			//compassData as a new arraylist.
			if (Page.Session[this.SessionKey] == null)
			{
				compassData = new ArrayList();
			}
			//if the session data contains compass data then
			//instantiate the compassData object with it
			else
			{
				compassData = (ArrayList)Page.Session[this.SessionKey];
			}

			//generate a new compass item object using the
			//parameters from the designer
			CompassItem newItem = new CompassItem();
			newItem.RawUrl = Page.Request.RawUrl;
			newItem.DisplayTitle = this.DisplayTitle;
			newItem.IconPath = this.IconPath;
			newItem.IconAlt = this.IconAlt;
			newItem.Target = this.LinkTarget.ToString();

			//set the value of the static variable that
			//indicates if object searches are case
			//sensitive
			CompassItem.CaseSensitiveUrlComparison = this.CaseSensitiveUrlComparison;

			//check if the newItem exists in the current
			//compass data this is possible as CompassItem
			//has an overriden .equals()
			if (compassData.Contains(newItem))
			{
				int index = compassData.IndexOf(newItem) + 1;
				int count = compassData.Count - index;
				compassData.RemoveRange(index, count);
			}
			//if the current item doesnt exist simply
			//add it to the end
			else
			{
				compassData.Add(newItem);
			}

			//place the compass data back into
			//the session
			Page.Session[this.SessionKey] = compassData;
		}
		/// <summary>
		/// Generates compass HTML for both vertical and horizontal styles
		/// 
		/// This demonstrates usage of HtmlTextWriter
		/// </summary>
		/// <param name="output">HtmlTextWriter to write into</param>
		public void GenerateCompass_HTML(HtmlTextWriter output, bool designer)
		{
			//declare and arraylist to contain the compass data
			//to process and instantiate with the data to render
			ArrayList compassData = GetRenderData(designer);

			//open the div tag to contain the compass
			//add set the base div parameters
			output.WriteBeginTag("div");

			//write the div name attribute if set
			if (this.DivName.Trim().Length > 0)
			{
				output.WriteAttribute("name", this._divName);
			}
			
			//write the div css class attribute if set
			if (this.CssClass.Trim().Length > 0)
			{
				output.WriteAttribute("class", this.CssClass);
			}

			//close the div tag
			output.Write(HtmlTextWriter.TagRightChar);

			//write line for code clarity
			output.WriteLine();

			//Check if the control has prefix code and write if required
			if (this.PrefixCode.Trim().Length > 0)
			{
				output.WriteFullBeginTag("a");
				output.Write(this.PrefixCode);
				output.WriteEndTag("a");
				output.WriteLine();
			}

			//cycle through each compass item and write this into
			//the output HtmlTextWriter
			foreach (CompassItem loopItem in compassData)
			{
				//get the index of the current item to prevent
				//constantly querying the .IndexOf function
				int itemIndex = compassData.IndexOf(loopItem);

				//write img tag if available and not suppressed
				if ((loopItem.IconPath.Trim().Length > 0) && (this.SuppressIcons == false))
				{
					output.WriteBeginTag("img");
					output.WriteAttribute("src", loopItem.IconPath);
					output.WriteAttribute("alt", loopItem.IconAlt);
					output.Write(HtmlTextWriter.SelfClosingTagEnd);
				}

				//check if we are currently working on the last item in
				//the compass if so render this without a link unless
				//otherwise specified through the LinkLastItem property
				if ((itemIndex == compassData.Count - 1) && (this.LinkLastItem == false))
				{
					//Add the seperator characters only if rendering a
					//verticalHTML compass
					if (this.CompassStyle == CompassStyles.VerticalHTML)
					{
						output.WriteFullBeginTag("a");
						output.Write(this.ItemSeperator);
						output.WriteEndTag("a");
					}
					
					//write the display title without a href link
					output.WriteFullBeginTag("a");
					output.Write(loopItem.DisplayTitle);
				}
				//create a linked compass item
				else
				{
					//Add the seperator characters only if rendering a
					//verticalHTML compass
					if (this.CompassStyle == CompassStyles.VerticalHTML)
					{
						output.WriteFullBeginTag("a");
						output.Write(this.ItemSeperator);
						output.WriteEndTag("a");
					}

					//write the item link and display title into the
					//the output stream
					output.WriteBeginTag("a");
					output.WriteAttribute("class", this.CssClassLink);
					output.WriteAttribute("href", loopItem.RawUrl);

					//only write the target attribute if it isnt overridden
					if (this.OverrideTarget == ItemLinkOverrideTargets.none)
					{
						output.WriteAttribute("target", loopItem.Target);
					}
					else
					{
						output.WriteAttribute("target", this.OverrideTarget.ToString());
					}

					output.Write(HtmlTextWriter.TagRightChar);
					output.Write(loopItem.DisplayTitle);
				}

				//write the currentpageindicator value if this is the last item
				//and the indicator has been assigned a value
				if ((compassData.IndexOf(loopItem) == compassData.Count - 1) && (this.CurrentPageIndicator.Trim().Length > 0))
				{
					output.Write(this.CurrentPageIndicator);
				}

				//close the item <a> tag
				output.WriteEndTag("a");

				//Add the seperator unless we are rendering 
				//the last item or a verticalHTML compass (in
				//case just write a <br>)
				if ((itemIndex < compassData.Count - 1) && (this.CompassStyle != CompassStyles.VerticalHTML))
				{
					output.WriteFullBeginTag("a");
					output.Write(this.ItemSeperator);
					output.WriteEndTag("a");
				}
				else
				{
					output.WriteFullBeginTag("br");
				}
				
				//write line for code clarity
				output.WriteLine();
			} //END - foreach (CompassItem loopItem in compassData)

			//Check if the control has a postfix entered
			if (this.PostfixCode.Trim().Length > 0)
			{
				output.WriteFullBeginTag("a");
				output.Write(this.PostfixCode);
				output.WriteEndTag("a");
			}

			output.WriteEndTag("div");
		}

		/// <summary>
		/// Generate compass HTML for a DHTML drop down menu
		/// 
		/// This demonstrates using strings instead of HtmlTextWriter
		/// </summary>
		/// <returns></returns>
		public void GenerateCompass_DHTMLMenu(HtmlTextWriter output, bool designer)
		{
			//declare and arraylist to contain the compass data
			//to process and instantiate with the data to render
			ArrayList compassData = GetRenderData(designer);

			output.WriteBeginTag("div");
			output.WriteAttribute("class", "menuBar");
			output.Write(HtmlTextWriter.TagRightChar);
			
			output.WriteBeginTag("a");
			output.WriteAttribute("class", "menuButton");
			output.WriteAttribute("href", "");
			output.WriteAttribute("onclick", "return buttonClick(event, 'menuCompassItem');");
			output.WriteAttribute("onmouseover", "buttonMouseover(event, 'menuCompassItem');");
			output.Write(HtmlTextWriter.TagRightChar);
			output.Write(this.PrefixCode);
			output.WriteEndTag("a");

			output.WriteEndTag("div");

			output.WriteBeginTag("div");
			output.WriteAttribute("id", "menuCompassItem");
			output.WriteAttribute("class", "menu");
			output.WriteAttribute("onmouseover", "menuMouseover(event)");
			output.Write(HtmlTextWriter.TagRightChar);

			foreach (CompassItem loopItem in compassData)
			{
				output.WriteBeginTag("a");
				output.WriteAttribute("class", "menuItem");
				output.WriteAttribute("href", loopItem.RawUrl);
				
				//only write the target attribute if it isnt overridden
				if (this.OverrideTarget == ItemLinkOverrideTargets.none)
				{
					output.WriteAttribute("target", loopItem.Target);
				}
				else
				{
					output.WriteAttribute("target", this.OverrideTarget.ToString());
				}

				output.Write(HtmlTextWriter.TagRightChar);

				if (loopItem.IconPath.Trim().Length > 0)
				{
					output.WriteBeginTag("img");
					output.WriteAttribute("src", loopItem.IconPath);
					output.WriteAttribute("alt", loopItem.IconAlt);
					output.Write(HtmlTextWriter.SelfClosingTagEnd);
				}
				
				output.Write(loopItem.DisplayTitle);

				//write the currentpageindicator value if this is the last item
				//and the indicator has been assigned a value
				if ((compassData.IndexOf(loopItem) == compassData.Count - 1) && (this.CurrentPageIndicator.Trim().Length > 0))
				{
					output.Write(this.CurrentPageIndicator);
				}

				output.WriteEndTag("a");
			}

			output.WriteEndTag("div");
		}

		/// <summary>
		/// Simple function to retrieve compass data from the session
		/// or create a dummy one for the VS .NET designer.
		/// </summary>
		/// <param name="designer"></param>
		/// <returns></returns>
		private ArrayList GetRenderData(bool designer)
		{
			ArrayList compassData;
			//check if VS .NET designer is calling the code
			//if it is then create a dummy compassData array
			//otherwise get the real data from the session
			if (designer)
			{
				compassData = new ArrayList();

				for(int i=0; i<this.MaxItems; i++)
				{
					CompassItem designerItem = new CompassItem();
					designerItem.DisplayTitle = string.Format("Link{0}", i);
					designerItem.IconPath = this.IconPath;
					designerItem.IconAlt = this.IconAlt;
					compassData.Add(designerItem);
				}
			}
			else
			{
				compassData = (ArrayList)Page.Session[this.SessionKey];

				//check that the total items does not
				//exceed the maximum, remove from the
				//start if it does, this doesnt effect
				//the session data which will allow a
				//user to back track
				if ((compassData.Count > this.MaxItems) && (this.MaxItems != 0))
				{
					ArrayList strippedData = new ArrayList();
					for (int index = compassData.Count - this.MaxItems; index < compassData.Count; index++)
					{
						strippedData.Add(compassData[index]);
					}
					return strippedData;
				}
			}

			return compassData;
		}

		/// <summary>
		/// Resets and removes all current compass items
		/// </summary>
		public void ResetCompass()
		{
			Page.Session[this.SessionKey] = new ArrayList();
		}

		/// <summary>
		/// Resets the compass with an array list of compass items
		/// </summary>
		/// <param name="compassData"></param>
		public void ResetCompass(ArrayList compassData)
		{
			Page.Session[this.SessionKey] = compassData;
		}

		/// <summary>
		/// Resets the compass to the given compass item
		/// </summary>
		/// <param name="item"></param>
		public void ResetCompass(CompassItem item)
		{
			ArrayList compassData = new ArrayList();
			compassData.Add(item);

			Page.Session[this.SessionKey] = compassData;
		}

		/// <summary>
		/// Retrieves the compass item at the given index (first occurence), returns null if not available
		/// </summary>
		/// <param name="index"></param>
		public CompassItem GetCompassItemAt(int index)
		{
			ArrayList compassData = (ArrayList)Page.Session[this.SessionKey];

			if (index > compassData.Count - 1)
			{
				return null;
			}

			return (CompassItem)compassData[index];

		}

		
		#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 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
Software Developer (Senior)
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions