1

I would like to send a Guid which should be associated with each radio button as eventargs when the user clicks checkout. I was able to achieve this functionality with just a RadioButtonList but I cannot use that here as the rest of the fields are pulled from the database.

There are numerous questions dealing with this type of topic but I could not find one that addresses exactly what I am trying to achieve.

I have the below list

<asp:Content ID="SubscriptionContent" ContentPlaceHolderID="MainContent" ViewStateMode="Enabled" runat="server">
    <asp:Panel ID="SubscriptionPanel" CssClass="shopping-cart" ViewStateMode="Enabled" runat="server">
        <asp:ListView ID="NewSubscription" runat="server">
            <LayoutTemplate>
                <table class="table">
                    <thead>
                        <th>Select a Subscription</th>
                        <th>Subscription Level
                        </th>
                        <th>Description
                        </th>
                        <th>Price
                        </th>
                    </thead>
                    <tbody>
                        <tr id="itemPlaceholder" runat="server" />
                    </tbody>
                </table>
            </LayoutTemplate>
            <ItemTemplate>
                <tr>
                    <td class="description"><asp:RadioButton ID="SubscriptionLevel" GroupName="SubscriptionRadio" Text='<%# Eval("SubscriptionLevel") %>' runat="server" /></td>
                    <td class="description"><asp:Label ID="Details" Text='<%# Eval("Details") %>' runat="server"></asp:Label></td>
                    <td class="price"><asp:Label runat="server" Text='<%# Eval("Price", "{0:C2}") %>'></asp:Label></td>
                    <asp:TextBox ID="Id" runat="server" Visible="false" Text='<%# Eval("Id") %>' />
                </tr>
            </ItemTemplate>
        </asp:ListView>

        <asp:Button ID="Subscribe" CssClass="btn btn-primary" runat="server" Text="<%$ Snippet: Ecommerce/ShoppingCart/CheckoutButtonLabel, Checkout %>" OnClick="BuySubscription" />
        <script type="text/javascript">
            $(document).ready(function () {
                $("input:radio").attr('name', 'SubscriptionRadio');//Override the naming that asp does                   
            });
        </script>
    </asp:Panel>
</asp:Content>

I am thinking that if I could update a hidden field with the corresponding guid value for each radio button and submit that when the user triggers BuySubscription. I am not sure how to do this though. Ultimately I just want the user to be able to select one of the subscription options and pass that guid back to the function.

Thank you in advance for any input.

Zach M.
  • 1,188
  • 7
  • 22
  • 46

1 Answers1

2

The first problem you're going to run into is that ASP.NET gives each <input type="radio" /> a different name, because it's in a different NamingContainer.

You've added some script to try to work around that by changing the name attribute on the client. Unfortunately, this won't work. When you post the form back to the server, ASP.NET will still be looking for a value using the generated name, which won't exist.

The quick and dirty solution would be to update your script to copy the value from the hidden TextBox to the value attribute of the radiobutton. You would then have to use Request.Form["SubscriptionRadio"] to retrieve the value of the selected option:

<ItemTemplate>
    <tr>
        <td class="description">
            <asp:RadioButton ID="SubscriptionLevel" runat="server"
                GroupName="SubscriptionRadio" 
                Text='<%# Eval("SubscriptionLevel") %>' 
            />
            <%-- NB: Can't set Visible="False", otherwise it's not rendered: --%>
            <asp:TextBox ID="Id" runat="server" 
                Style="display:none;"
                Text='<%# Eval("Id") %>' 
            />
        </td>
        ...
    </tr>
</ItemTemplate>
...
<script>
$(document).ready(function() {
    $("input:radio").each(function(){
        var me = $(this);
        var id = me.closest("tr").find("input[name$=Id]").val();
        me.attr("value", id);
        me.attr('name', 'SubscriptionRadio');
    });
});
</script>

Alternatively, you could use a custom RadioButton control which works within a data-bound control. I posted a simple example back in 2012: https://stackoverflow.com/a/13271444/124386

using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace SiteControls
{
    [ToolboxData("<{0}:ListRadioButton runat=\"server\" />")]
    public class ListRadioButton : RadioButton
    {
       private static readonly FieldInfo UniqueGroupNameField = FindUniqueGroupNameField();
       private string _uniqueGroupName;

       private static FieldInfo FindUniqueGroupNameField()
       {
          return typeof(RadioButton).GetField("_uniqueGroupName", 
             BindingFlags.NonPublic | BindingFlags.Instance);
       }

       public string Value
       {
           get { return Attributes["value"]; }
           set { Attributes["value"] = value; }
       }

       protected virtual string CreateUniqueGroupName()
       {
          string result = GroupName;
          if (string.IsNullOrEmpty(result))
          {
             result = ID;
          }
          if (string.IsNullOrEmpty(result))
          {
             result = UniqueID;
          }
          else
          {
             Control container = NamingContainer;
             if (container != null)
             {
                if (container is IDataItemContainer)
                {
                   container = container.NamingContainer ?? container;
                }

                result = container.UniqueID + base.IdSeparator + result;
             }
             else
             {
                string uniqueID = UniqueID;
                if (!string.IsNullOrEmpty(uniqueID))
                {
                   int index = uniqueID.LastIndexOf(base.IdSeparator);
                   if (index != -1)
                   {
                      result = uniqueID.Substring(0, 1 + index) + result;
                   }
                }
             }
          }

          return result;
       }

       private void EnsureUniqueGroupName()
       {
          if (_uniqueGroupName == null)
          {
             string value = CreateUniqueGroupName();
             if (UniqueGroupNameField != null) UniqueGroupNameField.SetValue(this, value);
             _uniqueGroupName = value;

             value = base.Attributes["value"];
             if (string.IsNullOrEmpty(value))
             {
                base.Attributes["value"] = UniqueID;
             }
          }
       }

       protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
       {
          EnsureUniqueGroupName();
          return base.LoadPostData(postDataKey, postCollection);
       }

       protected override void Render(HtmlTextWriter writer)
       {
          EnsureUniqueGroupName();
          base.Render(writer);
       }
    }
}

You can either register the control in the page markup:

<%@ Register tagPrefix="site" namespace="SiteControls" %>

or in the web.config file:

<?xml version="1.0"?>
<configuration>
    <system.web>
        <pages>
            <controls>
                <add 
                    tagPrefix="site" 
                    namespace="SiteControls"
                />
            </controls>
        </pages>
    </system.web>
</configuration>

With that in place, you can lose the script and the hidden TextBox:

<ItemTemplate>
    <tr>
        <td class="description"><site:ListRadioButton ID="SubscriptionLevel" runat="server"
            GroupName="SubscriptionRadio" 
            Text='<%# Eval("SubscriptionLevel") %>'
            Value='<%# Eval("Id") %>'
        /></td>
        ...

To find the selected item, you would then need to loop through the ListView's Items, find the RadioButton control, and look at the Checked property:

ListRadioButton selectedItem = NewSubscription.Items
    .Select(item => (ListRadioButton)item.FindControl("SubscriptionLevel"))
    .FirstOrDefault(radio => radio != null && radio.Checked);

string selectedValue = (selectedItem == null) ? null : selectedItem.Value;
Community
  • 1
  • 1
Richard Deeming
  • 29,830
  • 10
  • 79
  • 151
  • Would the code for the above extention to radiobutton just go in my CodeBehind file? I am `cannot resolve tag` exception for site and ListRadioButton, I am a bit new to straight ASP.NET and work primarily with MVC5 with Razor. Sorry in advance if this is a stupid question. – Zach M. Sep 10 '14 at 18:59
  • 1
    @ZachM.: The `ListRadioButton` would go in a separate class file. If you're using a web site project, you can add it to the `App_Code` folder; for a web application project, just add it as a class. You'd then need to register the control either in the page markup or the `web.config` file. – Richard Deeming Sep 10 '14 at 19:05
  • This worked like a charm, you Sir are a saint in the ASP.NET world. – Zach M. Sep 10 '14 at 19:20
  • You missed a close bracket in this code `$(document.ready(...` – intrepidis Sep 19 '14 at 14:35
  • @ChrisNash: Thanks - should be fixed now. – Richard Deeming Sep 19 '14 at 17:15
  • I can't seem to resolve the tag exception either. I've registered both ways suggested and create a new class file with the code. I used the SiteControls namespace. – default_noob_network Sep 25 '15 at 14:01
  • @Bubbas: Are you using a "web site" or a "web application" project? If it's an "application", you might need to add the assembly name to the registration. – Richard Deeming Sep 25 '15 at 22:01
  • Application. I don't understand what assembly you are talking about. – default_noob_network Sep 25 '15 at 22:12
  • @Bubbas: The `App_Code` folder doesn't work well with Web Application projects. Try creating a separate class library for the control, and adding a reference to that assembly. – Richard Deeming Sep 28 '15 at 10:53