I'm in between
contracts and jobs right now and I'm keeping very busy helping some buddies
convert a legacy automotive dispatching / billing application that was
originally written in Clipper / Foxpro to the .NET platform where it
will soon be operating over the Internet with SQL Server as a backend.
I have an equity interest in the deal and since my time is limited, I
need to gain economies of RAD wherever possible. One of the first things
I
realized
I'd need
for
this
app is
a menuing system - preferably one that doesn't take up a lot of real
estate in the browser, and can be customized to be different on each
page. But I also needed it to be extremely easy to wire up without any
custom coding because there are a lot of pages in the app.
So my first thought was a UserControl. There are two kinds, as we all
know - ServerControls, which are full-blown class libraries with Design-time
support, and .ascx User Controls, which aren't quite as scalable, but
are extremely easy to author. Since this is not going to be a high-traffic
site, I chose the ascx approach. By supplying a custom XML file containing
the menu items for each custom page, and using the same XSL for the transformation
and some script and css to handle everything else, all I need to do is
drag-and-drop my ascx user control on to the top of each page, set the
correct XML file for it, and I am "good to go!".
One of the coolest tricks with ascx controls, which replace the old-fashioned
Classic ASP functionality of the include file, is that you can write
up everything you need to get it working correctly as a regular WebForm
ASPX page. Once you are done and satisfied with your work, converting
it to an ASCX User Control is as simple as:
1) Remove all <html>, <body> and <form> tags.
2) Change the @Page directive to an @Control directive
3) Change the file extension from ASPX to ASCX, and
4) Have it derive
from System.Web.UI.UserControl instead of System.Web.UI.Page
Yup, it's really that simple! In this case, I needed
to get something working really fast, and so I looked around for some
decent XML / XSL menu stuff I could borrow from, rather than taking a
couple of hours of my valuable time writing it all from scratch. The
old MSDN top menu bar used by Microsoft around 1999 - 2000 turned out
to be just the ticket.
Now here's the code for the entire UserControl. Once
you step through it, you'll see how elegantly simple it all is:
FIrst the "page" portion:
<%@ Control language="c#" Codebehind="PageMenu.ascx.cs" AutoEventWireup="false" Inherits="MenuControl.PageMenu" %>
<link rel="stylesheet" type="text/css" href="menus.css">
<script language="javascript" src="menus.js"></script>
<asp:Label id="MenuPlaceHolder" runat="server">
Menu Table Goes Here
</asp:Label> |
The css references my stylesheet, and the script tag references my javascript
that handles opening and closing the menus and the mouseover events. And now, the codebehind class:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.IO;
using System.Text;
namespace MenuControl
{
public class PageMenu : System.Web.UI.UserControl
{
protected Label MenuPlaceHolder;
private string xmlFile = String.Empty;
private string xslFile = String.Empty;
public string XmlFileName
{
get { return(xmlFile); }
set {xmlFile = value;}
}
public string XslFileName
{
get { return(xslFile); }
set {xslFile = value;}
}
private void Page_Load(object sender, System.EventArgs e)
{
string XmlSystemFileName = Server.MapPath(xmlFile);
string XslSystemFileName = Server.MapPath(xslFile);
XslTransform xslt = new XslTransform();
xslt.Load(XslSystemFileName);
XPathDocument xpathdocument = new
XPathDocument(XmlSystemFileName);
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
xslt.Transform(xpathdocument, null, sw, null);
MenuPlaceHolder.Text = sb.ToString();
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
|
When you drop this onto a WebForm page, your HTML tags will look like
this (you have to manually put in the XML and XSL file attributes):
<%@ Register TagPrefix="uc1" TagName="PageMenu" Src="PageMenu.ascx" %>
<%@ Page language="c#" Codebehind="Default.aspx.cs" AutoEventWireup="false" Inherits="MenuControl.WebForm1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<uc1:PageMenu id=PageMenu1 runat="server" XmlFileName="Menu.xml" XslFileName="Menu.xsl"> </uc1:PageMenu>
</form>
</body>
</HTML>
|
The XML file structure for each custom menu is very simple:
<?xml version="1.0"?>
<TOPICLIST TYPE="MenuItems">
<TOPICS TYPE="DRIVER RECORDS">
<TOPIC>
<TITLE>Driver File Maintenance</TITLE>
<URL>/default.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Terminate /Reactivate Drivers</TITLE>
<URL>/Terminate.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Driver Types</TITLE>
<URL>DriverTypes.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Driver Reports</TITLE>
<URL>/DriverReports.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Driver Accounts</TITLE>
<URL>/DriverAccounts.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Driver Codes & Tables</TITLE>
<URL>/DriverCodes.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Driver Management Log</TITLE>
<URL>/DriverManagement.aspx</URL>
</TOPIC>
<TOPIC>
<TITLE>Driver / Vehicle Schedule</TITLE>
<URL>/DriverSchedule.aspx</URL>
</TOPIC>
</TOPICS>
<TOPICS TYPE="ORDERS DISPATCH SCHEDULING">
<TOPIC>
<TITLE>Attributes</TITLE>
<URL>/workshop/author/css/reference/attributes.asp</URL>
</TOPIC>
<TOPIC>
<TITLE>Length units</TITLE>
<URL>/workshop/author/css/reference/lengthunits.asp</URL>
</TOPIC> |
A couple of items to remember if you want to use this or customize it:
1) The XslTransform code is .NET Framework 1.1 compatible and
will not work in version 1.0 - you'll have to retrofit it if you are still
using the 1.0
version of the Framework.
2) The Solution and project files are Visual Studio.NET 2003. If you are
using Visual Studio.NET 2002, you'll need to either start a new web project
and bring in the files, or use my conversion utlity featured in a previous
article here to "back convert" it.
Would you like to see a sample in action? Just
click on over here. (I've only hooked up a couple of the links, so
don't be surprised if most items don't go anywhere). For those who are
interested, I also whipped up the same menu in the form of a ServerControl
(a separate article) which you can view
here.
Download the code that accompanies this article
| Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here. --><-- NOTE: Post QUESTIONS on FORUMS! |  | Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.
|