A Quirk of Controls in ASP.Net

As part of the legacy codebase I’m working with at the moment I have recently been required to edit a product listing page to do something simple; add an extra link underneath each product.

 

Interestingly enough the product listing page is constructed as a collection of System.Web.UI.Controls, generating an HTML structure directly in C# which is then styled after being rendered completely flat.

 

For example:, each item in the listing could look a bit like this
[csharp]
public class CodeControl : Control
{
protected override void CreateChildControls()
{
AddSomeStuff();
}

private void AddSomeStuff()
{
var image = new Image
{
ImageUrl = "http://memegenerator.net/cache/instances/250×250/8/8904/9118489.jpg",
Width = 250,
Height = 250
};
Controls.Add(image);

var hyperlink = new HyperLink { NavigateUrl = "http://memegenerator.net/", Text = "Business Cat" };
Controls.Add(hyperlink);

var title = new HtmlGenericControl();
title.Attributes.Add("class", "title");
title.InnerText = "£19.99";
Controls.Add(title);
}
}
[/csharp]
 

And then the code to render it would be something like:
[csharp]
private void PopulateContainerDiv()
{
var ul = new HtmlGenericControl("ul");

for (var i = 0; i < 10; i++)
{
// setup html nodes
var item = new CodeControl();
var li = new HtmlGenericControl("li");

// every 3rd li reset ul
if (i % 3 == 0) ul = new HtmlGenericControl("ul");

// add item to li
li.Controls.Add(item);

// add li to ul
ul.Controls.Add(li);

// add ul to div
containerDiv.Controls.Add(ul);
}
}
[/csharp]

The resulting HTML looks like:

[html]
<ul><li><img src="http://memegenerator.net/cache/instances/250×250/8/8904/9118489.jpg" style="height:250px;width:250px;" /><a href="http://memegenerator.net/">Business Cat</a><span class="title">&#163;19.99</span></li>
.. snip..
[/html]

And the page itself:

232111_codecontrol_blank_unstyled

I’ve never seen this approach before, but it does make sense; define the content, not the presentation. Then to make it look nicer we’ve got some css to arrange the list items and their content, something like:
[css]
ul { list-style:none; overflow: hidden; float: none; }
li { padding-bottom: 20px; float: left; }
a, .title { display: block; }
[/css]
Which results in the page looking a bit more like

232111_codecontrol_blank_styled

 

So that’s enough background on the existing page. I was (incorrectly, with hindsight, but that’s why we make mistakes right? How else would we learn? *ahem*..) attempting to implement a change that wrapped the contents of each li in a tag so that some jQuery could pick up the contents of that li and put them somewhere else on the page when a click was registered within the li.

So I did this:
[csharp highlight=”4,10,13″]
// setup html nodes
var item = new CodeControl();
var li = new HtmlGenericControl("li");
var form = new HtmlGenericControl("form");

// every 3rd li reset ul
if (i % 3 == 0) ul = new HtmlGenericControl("ul");

// add item to form
form.Controls.Add(item);

// add form to li
li.Controls.Add(form);

// add li to ul
ul.Controls.Add(li);

// add ul to div
containerDiv.Controls.Add(ul);
[/csharp]

I added in a <form> tag and put the control in there, then put the form in the li and the li in the ul. However, this resulted in the following HTML being rendered:

232111_codecontrol_elem_form

Eh? Why does the first <li> not have a <form> in there but the rest of them do? After loads of digging around my code and debugging I just tried something a bit random and changed it from a <form> to a <span>:
[csharp highlight=”4,10,13″]
// setup html nodes
var item = new CodeControl();
var li = new HtmlGenericControl("li");
var wrapper = new HtmlGenericControl("span");

// every 3rd li reset ul
if (i % 3 == 0) ul = new HtmlGenericControl("ul");

// add item to form
wrapper.Controls.Add(item);

// add form to li
li.Controls.Add(wrapper);

// add li to ul
ul.Controls.Add(li);

// add ul to div
containerDiv.Controls.Add(ul);
[/csharp]

Resulting in this HTML:

232111_codecontrol_elem_span

Wha? So if I use a <span> all is good and a <form> kills the first one? I don’t get it. I still don’t get it, and I’ve not had time to dig into it. in the end I just altered the jQuery to look for closest(‘span’) instead of closest(‘form’) and everything was peachy.

 

If anyone knows why this might happen, please do comment. It’s bugging me.

Leave a Reply

Your email address will not be published. Required fields are marked *