Styling the ASP.NET Wizard Control to have the Steps Across the top

The default style of the ASP.NET Wizard control is not the best. For the sidebar to work, and display all the wizard steps in a wizard requires quite a bit of space. And in all the wizards we end up creating, we don't want the user to jump from one step to the next using the sidebar. We make the user click from step to step linearly.

We've spent a little time lately changing the way the wizard looks and have come up with the following look and feel.

wizardDetails

The top HeaderTemplate contains the current step the user is on (derived from the WizardStep Title) and an indication of the total number of step (we use three different classes in indicate the current step, completed step and incomplete step). The "steps" also have tooltips added to let the user know what steps are coming. (See image below)

 Tooltip when Mouse hovers over the step number

The WizardStep in the image above is all the information regarding "Billing and Shipping" details (this is just standard Wizard Step stuff)

How its done

The markup for the wizard aspx code is as follows:

<asp:Wizard ID="wzd" runat="Server" Width="100%" DisplaySideBar="false">
    <HeaderTemplate>
        <table style="width: 100%" cellpadding="0" cellspacing="0">
            <tr>
                <td class="wizardTitle">
                    <%= wzd.ActiveStep.Title%>
                </td>
                <td>
                    <table style="width: 100%; border-collapse: collapse;">
                        <tr>
                            <td style="text-align: right">
                                <span class="wizardProgress">Steps:</span>
                            </td>
                            <asp:Repeater ID="SideBarList" runat="server">
                                <ItemTemplate>
                                    <td class="stepBreak">&nbsp;</td>
                                    <td class="<%# GetClassForWizardStep(Container.DataItem) %>" title="<%# DataBinder.Eval(Container, "DataItem.Name")%>">
                                        <%# wzd.WizardSteps.IndexOf(Container.DataItem as WizardStep) + 1 %>
                                    </td>
                                </ItemTemplate>
                            </asp:Repeater>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </HeaderTemplate>
    <SideBarTemplate>
    </SideBarTemplate>
    <WizardSteps> ... removed... </WizardSteps>
</asp:Wizard>

We add the following event handler to Page_Load in our code-behind

protected void Page_Load(object sender, EventArgs e)
{
    wzd.PreRender += new EventHandler(wzd_PreRender);
}

And have the following methods in the code-behind

protected void wzd_PreRender(object sender, EventArgs e)
{
    Repeater SideBarList = wzd.FindControl("HeaderContainer").FindControl("SideBarList") as Repeater;

    SideBarList.DataSource = wzd.WizardSteps;
    SideBarList.DataBind();

}

public string GetClassForWizardStep(object wizardStep)
{
    WizardStep step = wizardStep as WizardStep;

    if (step == null)
    {
        return "";
    }

    int stepIndex = wzd.WizardSteps.IndexOf(step);

    if (stepIndex < wzd.ActiveStepIndex)
    {
        return "stepCompleted";
    }
    else if (stepIndex > wzd.ActiveStepIndex)
    {
        return "stepNotCompleted";
    }
    else
    {
        return "stepCurrent";
    }
}

Explanation of the code above

When the wizard goes into PreRender, we bind the collection of WizardSteps to the Repeater in our HeaderTemplate

The "GetClassForWizardStep" is a helper method we have to determine what wizard step we're on, and render the appropriate class in the table cell.

Style Sheet Rules

The rules I'm using above to generate the page are as below...

/* WIZARD */
.stepNotCompleted
{
    background-color: rgb(153,153,153);
    width: 15px;
    border: 1px solid rgb(153,153,153);
    margin-right: 5px;
    color: White;
    font-family: Arial;
    font-size: 12px;
    text-align: center;
}

.stepCompleted
{
    background-color: #4d4d4d;
    width: 15px;
    border: 1px solid #4d4d4d;
    color: White;
    font-family: Arial;
    font-size: 12px;
    text-align: center;
}

.stepCurrent
{
    background-color: #e01122;
    width: 15px;
    border: 1px solid #e01122;
    color: White;
    font-family: Arial;
    font-size: 12px;
    font-weight: bold;
    text-align: center;
}

.stepBreak
{
    width: 3px;
    background-color: Transparent;
}

.wizardProgress
{
    padding-right: 10px;
    font-family: Arial;
    color: #333333;
    font-size: 12px;

}

.wizardTitle
{
    font-family: Arial;
    font-size: 120%;
    font-weight: bold;
    color: #333333;
    vertical-align: middle;

17 Comments

  • Thank you very much for posting this. This is a very useful option to be used with the Wizard. In the code above i am unable to find out which control refers to
    HeaderContainer
    Please help.

  • @Roopa - when the ASP.NET Wizard control renders, it puts everything that is inside the into a control called "HeaderContainer".

    Therefore, what I'm doing is looking for the asp:repeater called "SideBarList" inside the "HeaderContainer" (HeaderTemplate), which is inside the "wizard" control.

    We have to use a method similar to this to find controls inside the footer template (maybe I'll post on this soon)

  • That's what i was looking for, thank you very much.
    Just one question : is it possible to have the steps inside the header clickable, so that one can move directly to the desired step?

  • @Luca
    I changed the ItemTemplate (SideBarList) as follows
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;ItemTemplate&gt;
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;td class="stepBreak"&gt;&amp;nbsp;&lt;/td&gt;
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;td class="&lt;%# GetClassForWizardStep(Container.DataItem) %&gt;"&gt;
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;asp:LinkButton ID="btnWizStep" runat="server" OnClick="btnWizStep_Click" CssClass='&lt;%# GetClassForWizardStep(Container.DataItem) %&gt;' CommandArgument='&lt;%# wizCreateProject.WizardSteps.IndexOf(Container.DataItem as WizardStep) + 1%&gt;' CommandName='GoToStep' Text='&lt;%# wizCreateProject.WizardSteps.IndexOf(Container.DataItem as WizardStep) + 1%&gt;' /&gt;&lt;/td&gt;
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;/ItemTemplate&gt;
    My code behind has this method:
    &nbsp; &nbsp; &nbsp; &nbsp;protected void btnWizStep_Click(object sender, EventArgs e)
    &nbsp; &nbsp; &nbsp; &nbsp;{
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;wizCreateProject.ActiveStepIndex = int.Parse(((LinkButton)sender).CommandArgument) - 1;
    &nbsp; &nbsp; &nbsp; &nbsp;}
    This allowed me to capture the click events and navigate as needed. I hope this helps.

  • please do you now how to add another button either than next ,previous because we want to add save button which save the wizard current state and current step

  • @ahmed - I've posted a follow-up to your question here:
    http://weblogs.asp.net/grantbarrington/archive/2009/11/04/adding-changing-amp-finding-buttons-in-the-asp-net-wizard-control.aspx

    Hope this help you out.
    Grant

  • Thanks so much.I hope to you nice day.

  • Thank you very much, this is very slick!

  • Sir. Thanks. Truly Thanks.

  • hi i have unable to bind repeater using wizard steps ..i have use same code provided by you but when i run then Container.DataItem came null so it is unable to find indexof and through error...plz help

  • This is really good thanks

  • The highlighting of the steps is not being done. they are always 1 (is current) and 2...4 are stepNotCompleted. I know this due the fact that this code has been included in the markup. and it is only get executed once. The method (GetClass) is not affecting the styling.

    The solution to this is to put this code in the event (ActiveStepChanged) handler of the Wizard control:

    Repeater SideBarList = MyWizard.FindControl("HeaderContainer").FindControl("SideBarList") as Repeater;

    SideBarList.DataSource = MyWizard.WizardSteps;
    SideBarList.DataBind();

    - Yes, it is the same as the code in the PreRender event but in my case it is not get called but once.

  • Very nice post, exactly what i was looking for.
    Thanks

  • First, thanks for the great article, it's very helpful!

    Question: We create wizard pages dynamically based on content stored in database. One of QA testers created a wizard that had 40 pages. This resulted in the HeaderTemplate wrapping elements around the control. Can you provide an example of how to limit the number of elements that are drawn in the header (ellipsis, scroll, etc)?

    Thanks in advance

  • hey my css styling is not working...the step numbers are not showing up at the top..its jus plain black and white...please help me.

  • Thanks for this. I was trying to do some hack job in the sidebar, but this works really nice.

  • Works as a charm!

Comments have been disabled for this content.