65.9K
CodeProject is changing. Read more.
Home

User Controlled XML Menu For Site Navigation

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (3 votes)

Jul 9, 2009

CPOL

2 min read

viewsIcon

24956

downloadIcon

136

A fully customizable ASP.NET website menu that can be set by site admin to control user access.

DBDrivenMenu

Introduction

First of all, a huge thanks for giving a good response to my first article on CodeProject. In this article, I would illustrate the use of bit operations on integers to help us make a cool customizable menu. As always, my motto is minimum programming effort.

Background

You need to have a good understanding of the .NET CLR to better understand this article, as the concept relies heavily on it.

Let me first of all brief you on the use of the bitwise '&' operator. This operator is used to check the status of bits in integer values. Note the following about '&':

  1. 0 & 1 = 0
  2. 1 & 0 = 0
  3. 0 & 0 = 0
  4. 1 & 1 = 1

I would not go into the details of converting an integer to bits. But just note the following property:

10111011 &  00100000 = 00100000

I.e., 187 & 32 = 32

Using the Code

We would need to add SQL CLR to our code, as applying '&' to an integer is illegal in C#. We do this by using the Microsoft.SqlServer.Server namespace. For building up this menu, we assign a score in the form of 2^n to each item as:

<?xml version="1.0"?>
<SiteMenu>
    <MenuItem Title="A" URL="Page1.aspx" Value="1">
        <MenuItem Title="A.1" URL="Page1-1.aspx" Value="2" />
        <MenuItem Title="A.2" URL="Page1-2.aspx" Value="4" />
    </MenuItem>

    <MenuItem Title="B" URL="Page2.aspx" Value="8">
        <MenuItem Title="B.1" URL="Page2-1.aspx" Value="16"/>
        <MenuItem Title="B.2" URL="Page2-2.aspx" Value="32"/>
    </MenuItem>

    <MenuItem Title="C" URL="Page3.aspx" Value="64">
        <MenuItem Title="C.1" URL="Page3-1.aspx" Value="128"/>
        <MenuItem Title="C.2" URL="Page3-2.aspx" Value="256">
            <MenuItem Title="C.2.1" URL="Page3-2-1.aspx" Value="512"/>
            <MenuItem Title="C.2.2" URL="Page3-2-2.aspx" Value="1024">
                <MenuItem Title="C.2.2.1" URL="Page3-2-2.aspx" Value="2048"/>
            </MenuItem>
        </MenuItem>
    </MenuItem>
</SiteMenu>

We also assign a score to each user that is calculated as the sum of all the values of menu items that we want the user to see. Say, we want a user to only see the Main menu A with all the subitems, then the score will be 1+2+4 = 7. Note that you need to add up the main menu score, i.e., the score of A, B, or C if you want to use any of its child menus.

Now, let's jump to the last critical part, that is the CLR code that performs the '&' operation with the menu's values to see whether a user is eligible to see the navigation link or not.

[Microsoft.SqlServer.Server.SqlFunction()]
private bool Addable(int score, int valueToCheck)
{
    return ((score & valueToCheck) == valueToCheck);
}

Now, all we need is a record of score of each user and get the required XML data source to bind to a menu or tree. Each menu item's value is checked for its '&' ability with the total score, to see if it fits the user's menu criteria.

if (score != 0)
{
    datasetReadXML = new DataSet();
    datasetMenuItems = new DataSet();
    xdsMenuData = new XmlDataSource();

    ///Necessary for Displaying Updated Values.
    xdsMenuData.EnableCaching = false;
    xdsMenuData.EnableViewState = false;

    ///Read XML and prepare a menu data holder.
    datasetReadXML.ReadXml(pathToXML);
    datasetMenuItems = datasetReadXML.Clone();

    ///Get required menu items in the holder.
    foreach (DataRow drWorker in datasetReadXML.Tables[0].Rows)
        if (Addable(score, Convert.ToInt32(drWorker["Value"])))
            datasetMenuItems.Tables[0].ImportRow(drWorker);

    ///Make a StringWriter to read XML.
    stringWriter = new System.IO.StringWriter();
    datasetMenuItems.WriteXml(stringWriter, XmlWriteMode.IgnoreSchema);

    ///Populate XMLDataSource with XML.
    xdsMenuData.Data = stringWriter.ToString();

    ///Bind menu or tree.
    TreeMenu.DataSource = xdsMenuData;
    Menu.DataSource = xdsMenuData;

    this.DataBind();
}

I have used XML to keep track of user scores. However, you may use your own choice of database to do the same. All you need is a score. The attached code is self explanatory.

Points of Interest

Please remember to set the EnableCaching and EnableViewState properties of the XML data source to false for getting the updated values. I have seen a lot of people on various forums going nuts over it.