Thursday, April 17, 2008

The asp:radiobutton and the tale of the repeating control...

How many times have you needed to use a radio button in a repeating control, such as a GridView? Simple right? Just create a template column, drop in an asp:radiobutton and you are off. Sounds fantastic, yet it's a little more tricky than it first appears. The problem is that a radio button uses the name attribute to determine the group in which it belongs. That way it gives you the "only have one selected" effect.

The problem is that all server controls, housed within a repeater of sorts, are more than likely going to be created within an object implementing an interface called INamingContainer. This is a “token” interface that instructs the framework to create a unique name for any child controls that the control contains, at runtime, based on its own name (i.e. the name of the parent). That way, you can define a row template, have a data source with multiple rows, and not have a naming clash. Actually, its a requirement of the framework that all controls have a unique name. Technically, I am being incorrect, the framework requires all controls to have a unique ID, but the asp:radiobutton will generate a unique ID and a unique name (generally its exactly the same as the ID). However, as you can probably guess, the problem in this particular case, is the "name clash" effect on the name attribute is exactly the behaviour we need! Remember, it defines a “group” of radio buttons. The problem is that this behaviour is exactly what INamingContainer was created to avoid.

So enough chat... how do we get around this limitation without having to go to the trouble of rolling our own server control? The answer is actually quite simple. Don't use a server control.

For example, in the markup, within the repeater control (in this my case it was a GridView, if you were curious), define a templatefield as such:


<
asp:templatefield>
    <
itemtemplate>
         <
input name="rbSelected" type="radio" value="<%# Eval("ID") %>" />
    </
itemtemplate>
</
asp:templatefield>

As you can see, its a normal, plain old HTML element (admit it, you didn't even consider using one of these babies did you? :D ). Take note that it does not have a runat="server" attribute and is therefore (yes you guessed it) not a server control. This will make it exempt from the naming rules. I have however, used the databinding syntax to bind a useful value into the value property of the element, to demonstrate that while we are not using an asp:radiobutton server control, we can still make use of the funky databinding expressions that we know and love (and sometimes hate). This is because the row housing the element is a server control and when its databinding expressions are evaluated, databound values will be in the output. In my fictional case, this would be the primary key of the item I wanted to select. This could be anything you like or anything that is useful in your context.

Right, so the next question is: how do we access this value from the codebehind? This turns out to be very easy, just interrogate the Form value from the Page.Request object like so:

int value; 
if (!int.TryParse(this.Request.Form["rbSelected"], out value))
value = -1;
return value;














It's that simple.



However, to reiterate, the radio button in this example is not a server control. That means that we do loose a lot of the yummy-goodness that comes with having the control available on the server side (server side events to name just one). Therefore, this approach could be somewhat limited. However, for a simple option select, within a repeating control, you can not beat the simplicity and ease in which this solution can be implemented.



Enjoy!

No comments: