Click here to Skip to main content
15,905,008 members
Articles / Web Development / HTML

UserControl: ASP.NET 2.0 Wizard SideBar Replacement Navigation & How to

Rate me:
Please Sign up or sign in to vote.
2.79/5 (10 votes)
15 Nov 2006CPOL4 min read 69K   683   16   14
Simplifying customizable navigation with the supplied .NET Wizard control.

Sample Image - CodeProjectKferronWizardTopNav.gif

Introduction

This beautiful screenshot doesn't really do justice to what's going on here, but this article is written in response to some fatally tragic code I've seen floating around the net to deal with the ASP.NET Wizard control.

The Wizard control is actually a pretty sweet tool.. it captures some of the predictable logic in a step by step process pretty nicely... but...

...one of the inflexible pieces of its design is actually critically deficient in my opinion, and that is the SideBar. If you've ever looked through the Wizard members looking for a different placement of the SideBar, you will be as disappointed as I was. I decided to write this article when I noticed some people going way overboard with defeating this impediment in design. I want to present the solution I developed so that other people in this situation do not end up scrapping the use of the Wizard control, or end up writing a more complex solution than what is necessary.

Using the code

This control can be used in several ways right out of the box. One of the preferred ways I like to use it is actually in the <HeaderTemplate> of the Wizard:

ASP.NET
<asp:Wizard ID="Wizard1" runat="server" DisplaySideBar="False">
    <HeaderTemplate>
     <kferron:WizNav EnableViewState="true" ID="WizNav1" runat="server" />
    </HeaderTemplate>
    <WizardSteps>
        <asp:WizardStep ID="Step1" runat="server" Title="Step 1">
        Wow.
        </asp:WizardStep>
        <asp:WizardStep ID="Step2" runat="server" Title="Step 2">
        Amazing.
        </asp:WizardStep>
    </WizardSteps>
</asp:Wizard>

Another usage, which is also a viable option because of the flexibility of placement, is to "attach" this control to a wizard, using the WizardToNavigate property.

ASP.NET
<kferron:WizNav WizardToNavigate="Wizard2" 
     EnableViewState="true" ID="WizNav1" runat="server" />

<asp:Wizard ID="Wizard2" runat="server">
    <WizardSteps>
        <asp:WizardStep ID="Wiz2Step1" runat="server" Title="Step 1">
        </asp:WizardStep>
        <asp:WizardStep ID="Wiz2Step2" runat="server" Title="Step 2">
        </asp:WizardStep>
    </WizardSteps>
</asp:Wizard>

So anyway, you can see that you can move the control around in the designer and it will remain coupled with the Wizard.

About the code

I've stripped down my solution to the base elements for this article. There are many improvements that could and probably should be made before you just grab this control and run with it, but I wanted to focus on the fundamental topics here. I do, however, use a shell of a type that I make use of in the management of the navigation items. NavBarItem is its name, and it is as exciting as it sounds:

C#
public class NavBarItem
{
    private WizardStep _step;
    private string _id;

    public NavBarItem(string id, WizardStep step)
    {
        _id = id;
        _step = step;

    }

    public WizardStep Step
    {
        get { return _step; }
    }

    public string Id
    {
        get { return _id; }
    }

As you can see, I actually retain a direct reference to the WizardStep I am symbolizing with the NavBarItem. This is a design decision that you could alter quite easily if you feel more comfortable working directly with string identifiers.

Now, let's go over how NavBarItem is used. The following is in the Page_Load in WizNav.ascx.cs:

C#
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Collection <NavBarItem> navs = new Collection <NavBarItem>();

        foreach (WizardStep step in TargetWizard.WizardSteps)
        {
            navs.Add(new NavBarItem(step.Name,step));
        }

        NavBar.DataSource = navs;
        NavBar.DataBind();
    }
}

So if you're following along, this code accomplishes a couple of things for us. First of all, it uses TargetWizard to locate the actual Wizard control you are attempting to add navigation to. This allows us to iterate through the WizardSteps collection and keep our nav bar nice and dynamic. Secondly, we bind our collection of NavBarItems to a Repeater.

A Repeater is nice here because now we have a very customizable presentation of our navigation items in the wizard. The basic HTML in WizNav.ascx is like:

ASP.NET
<asp:Repeater ID="NavBar" runat="server" 
          OnItemCommand="NavBar_ItemCommand" 
          OnItemDataBound="NavBar_ItemDataBound"><ItemTemplate>
    <asp:LinkButton ID="LinkButton1" 
          runat="server">LinkButton</asp:LinkButton>
</ItemTemplate>
</asp:Repeater>

Notice that we are attaching to both OnItemCommand and OnItemDataBound. First, let's look at what we accomplish by encapsulating logic in these events, starting with OnItemDataBound.

C#
protected void NavBar_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
   NavBarItem it = e.Item.DataItem as NavBarItem;
   LinkButton lb = e.Item.FindControl("LinkButton1") as LinkButton;
   lb.Text = it.Id;
   lb.CommandArgument = it.Step.ID;
   lb.CommandName = switchStepsCommandName;
}

OK, this can be cleaned up a bit, but let's focus on the core logic. This event fires when an item is bound to the Repeater, and we actually have a reference to e.Item.DataItem here, which is convenient, because we can just cast to our NavBarItem.

It should be noted here, that in this event, all of our rendering properties of our navigation menu are accessible, including the current state of the wizard. For example, you could modify the logic here to make the current step the user is on not available in the nav bar, by doing:

C#
protected void NavBar_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    NavBarItem it = e.Item.DataItem as NavBarItem;
    LinkButton lb = e.Item.FindControl("LinkButton1") as LinkButton;

    if(it.Step == TargetWizard.ActiveStep) {
        lb.Visible = false;
    }
    else {
        lb.Text = it.Id;
        lb.CommandArgument = it.Step.ID;
        lb.CommandName = switchStepsCommandName;
    }     
}

Maybe someday if people are interested I can use all of the options that such a design allows for, but what I've given you is a paradigm that allows for all of the hooks you need.

We can also get a handle of any control we define in the Repeater. In this example, LinkButton1. LinkButton can be a powerful mechanism for dispatching commands. Notice that we are setting a commandName and a commandArgument, and our argument is the programmatic ID of the WizardStep that we have referenced in NavBarItem.

OnItemCommand of the Repeater fires when the LinkButton is clicked; the reasons why are outside the scope of this article, but if you're interested, this is a built-in bubbled event.

C#
public void NavBar_ItemCommand(object source, RepeaterCommandEventArgs e)
{
    if (e.CommandName == switchStepsCommandName)
    {
        TargetWizard.MoveTo((WizardStepBase)
             TargetWizard.FindControl((string)e.CommandArgument));

    }
}

So when this event fires, we check what command is attempting to run, and therefore know how to use the commandArgument, which as I stated above, is the identifier of the WizardStep.

Simple, easy to manage stuff. This should be one of the primary goals in your solutions.

Conclusion

I hope this helps some of you. This example shows how, with a properly constructed UserControl, taking advantage of what is already available to you is much better than reinventing the wheel. You can expand this UserControl quite easily.. perhaps you would want to use images in your navigation and have multistate images to highlight the user's progress. All you would need to do is modify the ItemTemplate in the Repeater. This is pretty simple stuff, but like I said, I was inspired to write this control when I saw numerous posts, some of which (in my opinion) are much uglier solutions to the same problem.

History

I wrote and added this article on 11/15/06.

License

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


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGetting links to change on Next and Previous Pin
rhuttenh16-Nov-09 11:17
rhuttenh16-Nov-09 11:17 
Questionhow i can put the new side bar vertical? Pin
eyalro6-Nov-07 23:13
eyalro6-Nov-07 23:13 
GeneralNavBarItem not required Pin
institute6-Nov-07 0:45
institute6-Nov-07 0:45 
Generalerror on TargetWizard. Change that property like that Pin
mehmetfatih15-Oct-07 23:02
mehmetfatih15-Oct-07 23:02 
protected Wizard TargetWizard
{
get
{

if (!String.IsNullOrEmpty(wizardToNavigate))
{
parentWizard = this.Parent.FindControl(wizardToNavigate) as Wizard;
}
else
{//this is to support putting this control in the headertemplate of the wizard
Control _par = this.Parent;
while (_par != null)
if (_par is Wizard)
{
parentWizard = _par as Wizard;
break;
}
else
_par = _par.Parent;
}

if (parentWizard != null)
return parentWizard;
else
throw new ArgumentOutOfRangeException(wizardToNavigate + " could not be found");


}

}
GeneralNice job. Pin
jlavigne16-May-07 12:52
jlavigne16-May-07 12:52 
GeneralItemDataBound fires for Header and Footer templates Pin
Lee Ryman13-Dec-06 18:41
Lee Ryman13-Dec-06 18:41 
QuestionHow to change border style of LinkButton for active step only? [modified] Pin
MollyKo3-Dec-06 21:32
MollyKo3-Dec-06 21:32 
GeneralRe: How to change border style of LinkButton for active step only? [modified] Pin
MollyKo4-Dec-06 20:28
MollyKo4-Dec-06 20:28 
GeneralRe: How to change border style of LinkButton for active step only? Pin
Kevin C Ferron9-Dec-06 0:11
Kevin C Ferron9-Dec-06 0:11 
Generalentry level article Pin
rajantawate1(http//www.tawateventures.com21-Nov-06 6:31
rajantawate1(http//www.tawateventures.com21-Nov-06 6:31 
GeneralRe: entry level article Pin
Kevin C Ferron9-Dec-06 0:12
Kevin C Ferron9-Dec-06 0:12 
GeneralRe: entry level article Pin
Lee Ryman13-Dec-06 18:49
Lee Ryman13-Dec-06 18:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.