Rendering ASP.NET Controls out of order
I receive a lot of comments and emails stemming from my series of articles on understanding the nuances of dealing with dynamic controls. It's interesting that so many of these requests revolve around very similar problems.
One such theme is where you would like to shuffle the controls on the page around a little bit. In a portal application like dotnetnuke, for example, the order modules are rendered on the page depends on custom configuration.
You can run into the nuances of dynamic controls if you plan on physically moving controls from one part of the control tree to another. For one, moving a control can cause it's unique ID to change. Normally that's ok, but sometimes something has already referenced that ID, and it's going to broken. Depending on when you move the control, you might be preventing postback data from loading correctly.
Not that there are no valid scenarios where you would want to move a control from one place to another. If you must, do so as early as possible in page lifecycle.
Or best of all -- avoid doing it in the first place.
In all of the comments/emails I received about this particular issue, the developer was making an assumption that doesn't have to be true. The assumption was that controls always render in the order they are in the tree, therefore, to change their order you must change the control tree.
Well here's a really simple control that shows that this is not true -- the Ordered Control:
namespace i88.Controls {
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
public enum RenderOrder {
Normal,
Reverse,
Random
}
public class Ordered : Control {
public RenderOrder Order {
get {
object o = ViewState["Order"];
if (o == null) {
return RenderOrder.Normal;
}
return (RenderOrder)o;
}
set {
ViewState["Order"] = value;
}
}
protected override void RenderChildren(HtmlTextWriter writer) {
switch (Order) {
case RenderOrder.Normal:
base.RenderChildren(writer);
break;
case RenderOrder.Reverse:
RenderReverse(writer);
break;
case RenderOrder.Random:
RenderRandom(writer);
break;
}
}
private void RenderRandom(HtmlTextWriter writer) {
ArrayList controls = new ArrayList(Controls);
Random r = new Random();
while (controls.Count > 0) {
int nextIndex = r.Next(0, controls.Count);
Control nextControl = (Control)controls[nextIndex];
nextControl.RenderControl(writer);
controls.Remove(nextControl);
}
}
private void RenderReverse(HtmlTextWriter writer) {
for (int i = Controls.Count - 1; i >= 0; i--) {
Controls[i].RenderControl(writer);
}
}
}
}
Ok, "Ordered" is a terrible name. I'm not good at names. Maybe it should be called the Unordered control, but whatever.
The point of the control is to allow you to change how it's children are ordered. Here's an example:
<asp:TextBox ID="TextBox1" runat="server" Text="TextBox 1" />
<asp:TextBox ID="TextBox2" runat="server" Text="TextBox 2" />
<asp:TextBox ID="TextBox3" runat="server" Text="TextBox 3" />
<asp:TextBox ID="TextBox4" runat="server" Text="TextBox 4" />
<asp:TextBox ID="TextBox5" runat="server" Text="TextBox 5" />
<asp:TextBox ID="TextBox6" runat="server" Text="TextBox 6" />
</i88:Ordered>
The controls are declared 1, 2, 3, but I've put the control in 'Reverse' mode, so they actually appear on the page in the reverse order. It also supports "Random".
But one of the key features of this control that helps drive home a point is that you can change the ordering mode on a postback, dynamically. The controls will happily rearrange themselves. No postback issues. No ViewState issues. No problems at all.
I haven't taken a look at dotnetnuke for a long, long time. But a long time ago when I did look at it, I noticed that it orders modules it contains by physically adding them to the page in the right order. When you change the ordering of modules on a page, it causes a postback. During the postback, the configuration file is changed, and then a Response.Redirect causes the page to reload from scratch.
Using this technique, that redirect wouldn't be necessary. Just render them in the new order.
Put this thing inside an ASP.NET AJAX Update Panel and put it in "Random" mode. Kind of fun to play with.
Enjoy.
PS: Published using the latest Live Writer Beta 3. Sweet.