On a recent project I investigated using the ASP.Wizard control to manage a simple page flow scenario. The site needed to comply with local government web guidelines and so the extensive use of tables and nested tables would have been an issue. So I experimented with how difficult it would be to write a CSS Adapter to output <div> tags instead.

After googling for a while it was apparent that a few people were asking about it but I couldn't find an example of anyone who had fully nailed it.

By default the wizard control renders to something like this.

<table cellspacing="0" cellpadding="0" border="0" id="myWizard" style="border-collapse: collapse;">
    <tr>
        <td style="height: 100%;">
            <table id="myWizard_SideBarContainer_SideBarList" cellspacing="0" border="0" style="border-collapse: collapse;">
                <tr>
                    <td style="font-weight: bold;">
                        <a id="myWizard_SideBarContainer_SideBarList_ctl00_SideBarButton" href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl00$SideBarButton','')">
                            Start Here</a>
                    </td>
                </tr>
                <tr>
                    <td>
                        <a id="myWizard_SideBarContainer_SideBarList_ctl01_SideBarButton" href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl01$SideBarButton','')">
                            Step 1</a>
                    </td>
                </tr>
                <tr>
                    <td>
                        <a id="myWizard_SideBarContainer_SideBarList_ctl02_SideBarButton" href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl02$SideBarButton','')">
                            Step 2</a>
                    </td>
                </tr>
                <tr>
                    <td>
                        <a id="myWizard_SideBarContainer_SideBarList_ctl03_SideBarButton" href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl03$SideBarButton','')">
                            The End</a>
                    </td>
                </tr>
            </table>
        </td>
        <td style="height: 100%;">
            <table cellspacing="0" cellpadding="0" border="0" style="height: 100%; width: 100%;
                border-collapse: collapse;">
                <tr>
                    <td>
                        Wizard Header
                    </td>
                </tr>
                <tr style="height: 100%;">
                    <td>
                        <h1>
                            Start Here</h1>
                    </td>
                </tr>
                <tr>
                    <td align="right">
                        <table cellspacing="5" cellpadding="5" border="0">
                            <tr>
                                <td align="right">
                                    <input type="submit" name="myWizard$StartNavigationTemplateContainerID$StartNextButton"
                                        value="Next" id="myWizard_StartNavigationTemplateContainerID_StartNextButton" />
                                </td>
                                <td align="right">
                                    <input type="submit" name="myWizard$StartNavigationTemplateContainerID$CancelButton"
                                        value="Cancel" id="myWizard_StartNavigationTemplateContainerID_CancelButton" />
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>

What I wanted was something like this.

<div class="wizard" id="myWizard">
    <div class="sidebar">
        <a class="active" href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl00$SideBarButton','')" id="myWizard_SideBarContainer_SideBarList_ctl00_SideBarButton">Start Here</a>
        <a href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl01$SideBarButton','')" id="myWizard_SideBarContainer_SideBarList_ctl01_SideBarButton">Step 1</a>
        <a href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl02$SideBarButton','')" id="myWizard_SideBarContainer_SideBarList_ctl02_SideBarButton">Step 2</a>
        <a href="javascript:__doPostBack('myWizard$SideBarContainer$SideBarList$ctl03$SideBarButton','')" id="myWizard_SideBarContainer_SideBarList_ctl03_SideBarButton">The End</a>
    </div>
    <div class="header">
        Wizard Header
    </div>
    <div class="step">
        <h1>Start Here</h1>            
    </div>
    <div class="nav">
        <input type="submit" value="Next" id="myWizard_StartNavigationTemplateContainerID_StartNextButton" name="myWizard$StartNavigationTemplateContainerID$StartNextButton" />
        <input type="submit" value="Cancel" id="myWizard_StartNavigationTemplateContainerID_CancelButton" name="myWizard$StartNavigationTemplateContainerID$CancelButton" />
    </div>
</div>

And that's exactly what my adapter does.

Then using only CSS styling you can control its layout and styles.

You'll need to download the CSS Friendly Control Adapters project from CodePlex first. Then download this zip file. It contains the class, WizardAdapter.cs and an example stylesheet, styles.css, that gives you an idea of the way in which you can style your wizard control using CSS.

Updates

  • 31 Aug 08 - thanks to Daniel Betts there's a VB version too¬†
  • 11 Oct 08 - updated the rendering logic to correctly recognised StepType property on WizardPages
Known Issues
  • Still not happy with the way TemplatedWizardPages render - they still appear in an embedded table

Note that the adapter ignores all style information you might add to the control - this is deliberate and an attempt to ensure users make use of CSS for this purpose rather than the embedded styles favoured by the ASP.NET controls.

kick it on DotNetKicks.com