Ajarn Mark Caldwell Blog

Bringing Business Sense to the IT World…

ASP.NET Multi-Select Radio Buttons

HERESY! you say, “Radio buttons are for single-select items!  If you want multi-select, use checkboxes!”  Well, I would agree, and that is why I consider this a significant bug that ASP.NET developers need to be aware of.  Here’s the situation.

If you use ASP:RadioButton controls on your WebForm, then you know that in order to get them to behave properly, that is, to define a group in which only one of them can be selected by the user, you use the Group attribute and set the same value on each one.  For example:

   1: <asp:RadioButton runat="server" ID="rdo1" Group="GroupName" checked="true" />
   2: <asp:RadioButton runat="server" ID="rdo2" Group="GroupName" />

With this configuration, the controls will render to the browser as HTML Input / Type=radio tags and when the user selects one, the browser will automatically deselect the other one so that only one can be selected (checked) at any time.

BUT, if you user server-side code to manipulate the Checked attribute of these controls, it is possible to set them both to believe that they are checked.

   1: rdo2.Checked = true;    // Does NOT change the Checked attribute of rdo1 to be false.

As long as you remain in server-side code, the system will believe that both radio buttons are checked (you can verify this in the debugger).  Therefore, if you later have code that looks like this

   1: if (rdo1.Checked)
   2: {
   3:     DoSomething1();
   4: }
   5: else
   6: {
   7:     DoSomethingElse();
   8: }

then it will always evaluate the condition to be true and take the first action.  The good news is that if you return to the client with multiple radio buttons checked, the browser tries to clean that up for you and make only one of them really checked.  It turns out that the last one on the screen wins, so in this case, you will in fact end up with rdo2 as checked, and if you then make a trip to the server to run the code above, it will appear to be working properly.  However, if your page initializes with rdo2 checked and in code you set rdo1 to checked also, then when you go back to the client, rdo2 will remain checked, again because it is the last one and the last one checked “wins”.

And this gets even uglier if you ever set these radio buttons to be disabled.  In that case, although the client browser renders the radio buttons as though only one of them is checked the system actually retains the value of both of them as checked, and your next trip to the server will really frustrate you because the browser showed rdo2 as checked, but your DoSomething1() routine keeps getting executed.

The following is sample code you can put into any WebForm to test this yourself.

   1: <body>
   2:     <form id="form1" runat="server">
   3:     <h1>Radio Button Test</h1>
   4:     <hr />
   5:     <asp:Button runat="server" ID="cmdBlankPostback" Text="Blank Postback" />
   6:     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
   7:     <asp:Button runat="server" ID="cmdEnable" Text="Enable All" OnClick="cmdEnable_Click" />
   8:     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
   9:     <asp:Button runat="server" ID="cmdDisable" Text="Disable All" OnClick="cmdDisable_Click" />
  10:     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  11:     <asp:Button runat="server" ID="cmdTest" Text="Test" OnClick="cmdTest_Click" />
  12:     <br /><br /><br />
  13:     <asp:RadioButton ID="rdoG1R1" GroupName="Group1" runat="server" Text="Group 1 Radio 1" Checked="true" /><br />
  14:     <asp:RadioButton ID="rdoG1R2" GroupName="Group1" runat="server" Text="Group 1 Radio 2" /><br />
  15:     <asp:RadioButton ID="rdoG1R3" GroupName="Group1" runat="server" Text="Group 1 Radio 3"  /><br />
  16:     <hr />
  17:     <asp:RadioButton ID="rdoG2R1" GroupName="Group2" runat="server" Text="Group 2 Radio 1" /><br />
  18:     <asp:RadioButton ID="rdoG2R2" GroupName="Group2" runat="server" Text="Group 2 Radio 2" Checked="true" /><br />
  20:     </form>
  21: </body>
   1: protected void Page_Load(object sender, EventArgs e)
   2: {
   4: }
   6: protected void cmdEnable_Click(object sender, EventArgs e)
   7: {
   8:     rdoG1R1.Enabled = true;
   9:     rdoG1R2.Enabled = true;
  10:     rdoG1R3.Enabled = true;
  11:     rdoG2R1.Enabled = true;
  12:     rdoG2R2.Enabled = true;
  13: }
  15: protected void cmdDisable_Click(object sender, EventArgs e)
  16: {
  17:     rdoG1R1.Enabled = false;
  18:     rdoG1R2.Enabled = false;
  19:     rdoG1R3.Enabled = false;
  20:     rdoG2R1.Enabled = false;
  21:     rdoG2R2.Enabled = false;
  22: }
  24: protected void cmdTest_Click(object sender, EventArgs e)
  25: {
  26:     rdoG1R2.Checked = true;
  27:     rdoG2R1.Checked = true;
  28: }
  30: protected void Page_PreRender(object sender, EventArgs e)
  31: {
  33: }

After you copy the markup and page-behind code into the appropriate files.  I recommend you set a breakpoint on Page_Load as well as cmdTest_Click, and add each of the radio button controls to the Watch list so that you can walk through the code and see exactly what is happening.  Use the Blank Postback button to cause a postback to the server so you can inspect things without making any changes.

The moral of the story is: if you do server-side manipulation of the Checked status of RadioButton controls, then you need to set ALL of the controls in a group whenever you want to change one.