Friday, January 14, 2005

XML Menu UserControl (ASP.NET)

A UserControl for ASP.NET that displays a menu generated from an XML file. Very basic. If the XML file is missing (or not valid XML markup) nothing renders.

UserControl: menu.ascx

<%@ Control Language="C#" src="menu.ascx.cs" Inherits="Menu" %>
<asp:Repeater runat="server" id="rptMenu">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
	 <li><asp:HyperLink runat="server" Tooltip='<%# DataBinder.Eval(Container.DataItem, "Tooltip") %>' NavigateUrl='<%# DataBinder.Eval(Container.DataItem, "NavigateUrl") %>' Text='<%# DataBinder.Eval(Container.DataItem, "Text") %>' /></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>

Code Behind: menu.ascx.cs

using System;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;

public class Menu : UserControl
{

	#region Web Form Designer generated code
	override protected void OnInit(EventArgs e)
	{
		//
		// CODEGEN: This call is required by the ASP.NET Web Form Designer.
		//
		InitializeComponent();
		base.OnInit(e);
	}
	
	/// <summary>
	/// Required method for Designer support - do not modify
	/// the contents of this method with the code editor.
	/// </summary>
	private void InitializeComponent()
	{
		this.Load += new System.EventHandler(this.Page_Load);
	}
	#endregion
	
	// the xml file to load the data from
	public string XmlFile = "menu.xml";
	// data table for storing menu data
	protected DataTable MenuData;
	// menu repeater
	protected Repeater rptMenu;
	
	private void Page_Load(object sender, EventArgs e)
	{
		SetupDataTable();
		if (!Page.IsPostBack) {
			SetupMenu();
		}
	}
	// sets up the data table that is to be bound to the repeater
	private void SetupDataTable()
	{
		MenuData = new DataTable();
		MenuData.Columns.Add(new DataColumn("Text", System.Type.GetType("System.String")));
		MenuData.Columns.Add(new DataColumn("NavigateUrl", System.Type.GetType("System.String")));
		MenuData.Columns.Add(new DataColumn("Tooltip", System.Type.GetType("System.String")));
	}
	private void SetupMenu()
	{
		DataRow NewRow;
		XmlTextReader reader = null;
		try {
			reader = new XmlTextReader(MapPath(ResolveUrl(XmlFile)));
			// ignore whitespace
			reader.WhitespaceHandling = WhitespaceHandling.None;
			// move to first node
			reader.MoveToContent();
			// loop until there is nothing left to read and the end node hasn't been reached
			while (reader.Read()) {
				// if current node is not menu item, move to next node
				if(reader.Name != "MenuItem") continue;
				// if same as current page, move to next node
				if(SameAsCurrent(reader.GetAttribute("page"))) continue;
				NewRow = MenuData.NewRow();
				NewRow["Text"] = reader.GetAttribute("text");
				NewRow["NavigateUrl"] = reader.GetAttribute("page");
				NewRow["Tooltip"] = reader.GetAttribute("tooltip");
				MenuData.Rows.Add(NewRow);
			}
			// close reader
			reader.Close();
			// set datasource to populated data table and bind
			rptMenu.DataSource = MenuData;
			rptMenu.DataBind();
		} catch (System.Exception ex) {
			// close reader if error has occured
			if (reader != null) {
				reader.Close();
			}
			Trace.Write(ex.Source,ex.Message);
		}
	}
	// same as current page
	private bool SameAsCurrent(string page)
	{
		// use RawUrl to include query string when resolving, Path to exclude it
		return ResolveUrl(page)==ResolveUrl(HttpContext.Current.Request.Path);
	}
}

Menu Items: test.xml

<?xml version="1.0" encoding="utf-8"?>
<Menu>
	<MenuItem page="~/1.aspx" tooltip="Tooltip 1" text="Item 1" />
	<MenuItem page="~/2.aspx" tooltip="Tooltip 2" text="Item 2" />
	<MenuItem page="~/3.aspx" tooltip="Tooltip 3" text="Item 3" />
</Menu>

Test Page: menutest.aspx

<%@ Page Language="C#" %>
<%@ Register TagPrefix="Menu" TagName="Test" src="menu.ascx" %>
<script runat="server">
	private void DoPostBack(object sender, EventArgs e)
	{
		MyLabel.Text = "Posted back";
	}
</script>
<html>
<head>
<title>XML Menu</title>
</head>
<body>
<form runat="server">
<Menu:Test XmlFile="test.xml" runat="server" />
<asp:Button runat="server" OnClick="DoPostBack" Text="Button" />
<asp:Label id="MyLabel" runat="server" />
</form>
</body>
</html>

3 comments:

Anonymous said...

what is this

Sam said...

Create the files menu.ascx, menu.ascx.cs, test.xml, menutest.aspx (with the associated code) in the same directory of on ASP.NET application and run menutest.aspx

Just shows how you can parse XML with ASP.NET and display the contents in menu form. Rather than editing the page to add new items, just edit the XML file. As it is a UserControl it can be used on multiple pages.

Anonymous said...

Thanks for a good tutorial

Could this be used to make a dropdown menu and how would you do it..

Lasse/Denmark