Wednesday, September 14, 2011

ASP.NET 4.0 Menu Outputs a Table or Anchors Instead of an Unordered List

 

The Short Answer (In My Case, Anyway)

A configuration setting in web.config is causing the .NET 3.5 behaviour of outputting a table or a set of anchors.  The setting is:

<pages controlRenderingCompatibilityVersion="3.5" />

Remove this to get the desired 4.0 behaviour of building an unordered list. Visual Studio added this because you created your project using .NET 3.5 then changed the version to 4.0. Um, thanks, VS.


The Long Answer


I’ve spent the last few days trying to understand why my instance of a Menu control was creating a table and not an unordered list. It’s a .NET 4.0 site and every test 4.0 app I’ve created has produced an unordered list.


I thought maybe it was because I was using a SiteMapDataSource. I tried defining the items within the Menu control, but this made no difference.


Maybe it was because I was using WCSF to create populate the SiteMapDataSource. So I created a new WCSF-based project and found it output an unordered list. So nothing in WCSF was triggering the behaviour. I studied the code generated in a new ASP.NET 4.0 website (the non-empty template) and in a new WCSF and I couldn’t find anything different.


Some Googling finally revealed that certain controls look at a configuration setting called controlRenderingCompatibilityVersion to decide how to act. This isn’t normally present. It gets set when you change your web project from 3.5 to 4.0. Once I read this I remembered accidentally creating my app targeting 3.5 and then switching to 4.0.


This appears to be the only significant change to a web app when switched to 4.0.  Lots of entries disappear from web.config. This is probably because 3.5 is really a bunch of extensions to 2.0, and all of the extensions are now just part of 4.0. 


This config setting sounds like it will make Menu act the same way under 4.0 as 3.5, but it turns out that’s not true.  Native 3.5, 3.5 “compatibility” under 4.0 and native 4.0 Menus all produce unique HTML.  Let’s have a look.


Create a new 3.5 ASP.NET website (the non-empty template) in Visual Studio 2010 and add:


  1. A Sitemap file with the default name of Web.Sitemap.  Make the contents
    <?xml version="1.0" encoding="utf-8" ?>
    <siteMap>
    <siteMapNode title="Home" >
    <siteMapNode title="Services" >
    <siteMapNode title="Training" url="~/Training.aspx"/>
    </siteMapNode>
    <siteMapNode title="Services" >
    <siteMapNode title="Training" url="~/Training2.aspx"/>
    </siteMapNode>
    </siteMapNode>
    </siteMap>
    

  2. A SiteMapDataSouce in Default.aspx, e.g.
    <asp:SiteMapDataSource ID="SiteMapDataSource1" Runat="server" ShowStartingNode="false" />

  3. A Menu control in Default.aspx, e.g.
    <asp:Menu runat="server" ID="MenuTest" DataSourceID="SiteMapDataSource1" Orientation="Horizontal">

Now run it and take a look at the source:

<span><a class="MenuTest_1" href="javascript:__doPostBack('MenuTest','oServices')">Services<img src="/WebResource.axd?d=wAEvwChqppIeDD27eosuLSSKK1v72lmXTc50uPK75upm7hfsxNhE9GeVh7SSC19_P7eqejSkCMIMAhKEvlwbQTVPYtk1&amp;t=634244938920000000" alt="Expand Services" align="absmiddle" style="border-width:0px;" /></a></span>
<span><a class="MenuTest_1" href="javascript:__doPostBack('MenuTest','oServices')">Services<img src="/WebResource.axd?d=wAEvwChqppIeDD27eosuLSSKK1v72lmXTc50uPK75upm7hfsxNhE9GeVh7SSC19_P7eqejSkCMIMAhKEvlwbQTVPYtk1&amp;t=634244938920000000" alt="Expand Services" align="absmiddle" style="border-width:0px;" /></a></span>

A messy set of spans and anchor tags.

Upgrade the project to 4.0, run it again and look at the source code:

<table id="MenuTest" class="MenuTest_2" cellpadding="0" cellspacing="0" border="0">
<tr>
<td onmouseover="Menu_HoverStatic(this)" onmouseout="Menu_Unhover(this)" onkeyup="Menu_Key(event)" id="MenuTestn0">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="white-space:nowrap;"><a class="MenuTest_1" href="#" style="cursor:text;">Services</a></td>
<td style="width:0;"><img src="/WebResource.axd?d=Jl7bM-JFbdoWBEh7C2q3EyZR8K6O6d4YLATzREzpY2ex3k5XYksROon9Z1cxmO8alNxDSkn-ofGqiqw-z6u74-AwPDdR9USUgzqIlqL9sBA1&amp;t=634486182303750217" alt="Expand Services" style="border-style:none;vertical-align:middle;" /></td>
</tr>
</table>
</td>
<td onmouseover="Menu_HoverStatic(this)" onmouseout="Menu_Unhover(this)" onkeyup="Menu_Key(event)" id="MenuTestn1">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="white-space:nowrap;"><a class="MenuTest_1" href="#" style="cursor:text;">Services</a></td>
<td style="width:0;"><img src="/WebResource.axd?d=Jl7bM-JFbdoWBEh7C2q3EyZR8K6O6d4YLATzREzpY2ex3k5XYksROon9Z1cxmO8alNxDSkn-ofGqiqw-z6u74-AwPDdR9USUgzqIlqL9sBA1&amp;t=634486182303750217" alt="Expand Services" style="border-style:none;vertical-align:middle;" /></td>
</tr>
</table>

Now it’s a messy table.

Finally, remove the controlRenderingCompatibilityVersion setting from web.config and look at the source:

<ul class="level1">
<li><a>Services</a><ul class="level2">
<li><a class="level2" href="/Training.aspx">Training</a></li>
</ul></li><li><a>Services</a><ul class="level2">
<li><a class="level2" href="/Training2.aspx">Training</a></li>
</ul>
</li>
</ul>

A nice, clean unordered list.

I really don’t understand why the controlRenderingCompatibilityVersion setting doesn’t produce exactly what a 3.5 Menu does, but I don’t care that much. I just want my unordered list, and now I know how to get it.

This is the kind of thing for which people criticize ASP.NET WebForms. Some people want almost complete control over the HTML that a framework produces, which leads them to ASP.NET MVC, Ruby on Rails et al. Those are great, but WebForms hasn’t grated on me that much in my life so I‘m nowhere near ready to give up on it. People’s lack of understanding of event-based web programming and getting data directly from the page code-behind has lead to far worse headaches for me.

MS Test, the unit testing framework built into Visual Studio, on the other hand…