ASP.NET RadioButton 与名称(组名)混淆

Posted

技术标签:

【中文标题】ASP.NET RadioButton 与名称(组名)混淆【英文标题】:ASP.NET RadioButton messing with the name (groupname) 【发布时间】:2010-09-27 12:05:12 【问题描述】:

我有一个模板化控件(中继器),列出了一些文本和其他标记。每个项目都有一个与之关联的单选按钮,使用户可以选择中继器创建的项目之一。

转发器写入单选按钮,设置其 id 和生成的名称,使用默认的 ASP.NET 命名约定,使每个单选按钮成为一个完整的“组”。这意味着所有单选按钮都是相互独立的,不幸的是,这再次意味着我可以同时选择所有单选按钮。单选按钮具有用于设置通用名称的巧妙属性“组名”,因此它们被组合在一起,因此应该相互依赖(所以我一次只能选择一个)。问题是 - 这不起作用 - 转发器确保 id 和名称(控制分组)不同。

由于我使用了中继器(可能是列表视图或任何其他模板化数据绑定控件),我无法使用 RadioButtonList。那我该怎么办?

我知道我以前遇到过这个问题并解决了它。我知道几乎每个 ASP.NET 程序员都必须拥有它,那么为什么我不能 google 并找到解决问题的可靠方法呢?我遇到了通过 javascript 强制分组的解决方案(丑陋!),甚至将单选按钮作为非服务器控件处理,迫使我执行 Request.Form[name] 来读取状态。我还尝试在 PreRender 事件上覆盖 name 属性 - 不幸的是,拥有页面和 masterpage 再次覆盖此名称以反映完整的 id/name,所以我最终得到了相同的错误结果。

如果您没有比我发布的更好的解决方案,仍然非常欢迎您发表您的想法 - 至少我会知道我的朋友 'jack' 是正确的,关于 ASP.NET 有时是多么混乱;)

【问题讨论】:

见:BUG: Radio Buttons Are Not Mutually Exclusive When Used in a Repeater Server Control。这简直太疯狂了……这是自 1.0 框架以来的一个错误,它的影响如此之大。我需要继承单选按钮并重写几个方法以使其工作(对于我当前的 ASP.NET 版本)。什么给了? 【参考方案1】:

ASP.NET 提示:在中继器中使用 RadioButton 控件

这是 JavaScript 函数的代码:

function SetUniqueRadioButton(nameregex, current)

   re = new RegExp(nameregex);
   for(i = 0; i < document.forms[0].elements.length; i++)
   
      elm = document.forms[0].elements[i]
      if (elm.type == 'radio')
      
         if (re.test(elm.name))
         
            elm.checked = false;
         
      
   
   current.checked = true;

代码通过ItemDataBound事件链接到Repeater。要使其正常工作,您需要知道Repeater 控件的名称,以及您分配给RadioButtons 的GroupName。在这种情况下,我使用 rptPortfolios 作为中继器的名称,并使用 Portfolios 作为组名:

protected void rptPortfolios_ItemDataBound(object sender,
                                           RepeaterItemEventArgs e)

   if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType
      != ListItemType.AlternatingItem)
      return;

   RadioButton rdo = (RadioButton)e.Item.FindControl("rdoSelected");
   string script =
      "SetUniqueRadioButton('rptPortfolios.*Portfolios',this)";
   rdo.Attributes.Add("onclick", script);


参考号:http://www.codeguru.com/csharp/csharp/cs_controls/custom/article.php/c12371/

【讨论】:

很好的解决方案!效果很好。【参考方案2】:

谷歌福:asp.net radiobutton repeater problem

确实是 id 修改的不幸后果。我的想法是创建一个 - 或从众多可用的 - 自定义控件中选择一个,以在客户端上添加对同名的支持。

【讨论】:

【参考方案3】:

Vladimir Smirnov 已经创建了一个很好的自定义控件来解决这个问题。我们一直在我们的项目中使用GroupRadioButton,它与在中继器内部创建的单选按钮和中继器外部的其他单选按钮都属于同一组。

【讨论】:

【参考方案4】:

我使用 jQuery 脚本:

<script type="text/javascript">
    function norm_radio_name() 
        $("[type=radio]").each(function (i) 
            var name = $(this).attr("name");
            var splitted = name.split("$");
            $(this).attr("name", splitted[splitted.length - 1]);
        );
    ;

    $(document).ready(function () 
        norm_radio_name();
    );

    // for UpdatePannel
    var prm = Sys.WebForms.PageRequestManager.getInstance();

    prm.add_endRequest(function () 
        norm_radio_name();
    );
</script>

【讨论】:

当心!如果您使用此解决方案,您将无法在代码隐藏中处理用户更改,因为服务器期望的名称/值对永远不会更改。 在页面加载期间选中/取消选中的任何内容都将保持选中/取消选中状态,尽管有用户操作。【参考方案5】:

我知道这是一篇旧文章,但这是我最终使用列表视图所做的。我的列表视图绑定在 VB 代码隐藏中,所以我不确定这是否适用于中继器,但我想它可能是相似的。

我所做的是使用取消选择任何其他单选按钮的功能来处理单选按钮的 OnCheckChanged 事件。然后,当我离开该页面时,我会寻找选中的单选按钮。

此解决方案避免了 JavaScript 和 jQuery,并且完全忽略了 GroupName 问题。这并不理想,但它的功能与(我)预期的一样。我希望它对其他人有帮助。

标记:

<asp:ListView ID="lvw" runat="server">
  <LayoutTemplate>`
    <table>
      <th>Radio</th>
      <tr id="itemPlaceholder"></tr>
    </table>
  </LayoutTemplate>
  <ItemTemplate>
    <tr>
      <td><asp:RadioButton ID="rdbSelect" runat="server" AutoPostBack="true"
           OnCheckedChanged="rdbSelect_Changed"/></td>
    </tr>
  </ItemTemplate>
</asp:ListView>

代码:

Protected Sub rdbSelect_Changed(ByVal sender As Object, ByVal e As System.EventArgs)

  Dim rb1 As RadioButton = CType(sender, RadioButton)

  For Each row As ListViewItem In lvw.Items
    Dim rb As RadioButton = row.FindControl("rdbSelect")
      If rb IsNot Nothing AndAlso rb.Checked Then
         rb.Checked = False
      End If
  Next
  rb1.Checked = True
End Sub

然后当点击提交按钮时:

Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs)  Handles btnSubmit.Click

  For Each row As ListViewItem In lvw.Items
    Dim rb As RadioButton = row.FindControl("rdbSelect")
        Dim lbl As Label
        If rb IsNot Nothing AndAlso rb.Checked = True Then
            lbl = row.FindControl("StudentID")
            Session("StudentID") = lbl.Text
        End If
    Next

    Response.Redirect("~/TransferStudent.aspx")
End Sub

【讨论】:

不错的解决方案!我一直在寻找一种不需要 javascript 的解决方案,并且从未想过只是取消选择所有单选按钮 OnCheckChanged。干杯!【参考方案6】:

这可能会好一点..

我有一个用户控件,它本质上是中继器内的一组单选按钮,用户控件的每个实例都有一个名为 FilterTitle 的公共属性,每个实例都是唯一的。

将这两个属性添加到您的单选按钮中,将 FilterTitle 替换为您自己的公共属性名称

onclick='<%# "$(\"input[name$=btngroup_" + FilterTitle + "]\").removeAttr(\"checked\"); $(this).attr(\"checked\",\"checked\");" %>' GroupName='<%# "btngroup_" + FilterTitle  %>'

更简单..

onclick="$('input[name$=btngroup1]').removeAttr('checked'); $(this).attr('checked','checked');" GroupName="btngroup1"

【讨论】:

【参考方案7】:

为了完整起见,这是一个纯 Javascript 解决方案。

只需将此onclick 属性添加到您的RadioButton 元素(将GroupName 替换为您的RadioButton 的GroupName):

<asp:RadioButton ... GroupName="GroupName" onclick="SelectRadioButton('GroupName$',this)" ... />

并在您的页面中包含此 Javascript:

<script type="text/javascript">
function SelectRadioButton(regexPattern, selectedRadioButton)
    
        regex = new RegExp(regexPattern);
        for (i = 0; i < document.forms[0].elements.length; i++)
        
            element = document.forms[0].elements[i];
            if (element.type == 'radio' && regex.test(element.name))
            
                element.checked = false;
            
        
        selectedRadioButton.checked = true;
    
</script>

【讨论】:

【参考方案8】:

创建自定义控件并将 UniqueID 覆盖为列表视图 UniqueID + GroupName

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MYCONTROLS


[ToolboxData("<0:MYRADIOBUTTON runat=server></0:MYRADIOBUTTON >")]
public class MYRADIOBUTTON : RadioButton

    public override string UniqueID
    
        get
        
            string uid = base.UniqueID;
            //Fix groupname in lisview
            if (this.Parent is ListViewDataItem && GroupName != "")
            
                uid = this.Parent.UniqueID;
                uid = uid.Remove(uid.LastIndexOf('$'));
                uid += "$" + GroupName;

            
            return uid;


        
    



它是一个继承自 RadioButton 的自定义控件(公共类 MYRADIOBUTTON : RadioButton)。 如果你在课堂上什么都不做,你会得到一个普通的 RadioButton。覆盖 UniqueId 您可以更改名称属性呈现方式的逻辑。 为了仍然保持页面上其他控件的名称(列表视图之外)唯一,您可以从 ListView 获取 UniqueId 并将 GroupName 添加到该名称并将其添加到 RadioButton。

这解决了在列表视图的不同行上分组单选按钮的问题。您可能希望使用属性添加更多逻辑来打开/关闭此功能,使其表现得像普通的 RadioButton。

【讨论】:

对您的代码在做什么的一些解释将有助于此答案的未来读者 它的第一行创建一个自定义控件并将 UniqueID 覆盖到列表视图 UniqueID + GroupName【参考方案9】:

我知道这个问题有点老了,但我认为它可能会对某人有所帮助,因此发布我对这个问题的解决方案。

这个问题有两个部分:

    防止一次选择多个单选按钮。 要知道在服务器端代码中单击了哪个单选按钮。

Repeater 中的单选按钮也有类似的问题。我在这里找到了部分解决方案: Simple fix for Radio Button controls in an ASP.NET Repeater using jQuery

请阅读以上文章以了解该问题。我参考了这篇文章,这是很好的帮助。如上所述,这个问题有两个部分,首先是防止一次选择多个单选按钮。其次,要知道在服务器端代码中单击了哪个单选按钮。上述文章中发布的解决方案仅适用于第一部分。但是,那里编写的代码以及那里发布的解决方案更新对我不起作用。所以我不得不对其进行一些修改以使其正常工作。这是我的解决方案。

我想用投票按钮创建投票。我的单选按钮 (ID) 的名称是 PollGroupValue,其中 Groupname 在转发器中设置为 PollGroup。记住 ASP.net 中的 Groupname 属性在生成的 html 中呈现为 name 属性。我的代码如下:

<script type="text/javascript">

/*  Step-01: */
$(document).ready(function () 

    /*  Step-02: */
    $("[name*='PollGroup']").each(function () 
        $(this).attr('ci-name', $(this).attr('name'));
    );

    /*  Step-03: */
    $("[name*='PollGroup']").attr("name", $("[name*='PollGroup']").attr("name"));

    $('#Poll1_BtnVote').click(function (e) 

        /* Step - 04:  */
        if ($("[name*='PollGroup']").filter(':checked').length == 0) 
            alert('Please select an option.');
            e.preventDefault();
        

        /* Step - 05:  */
        $("[name*='PollGroup']").each(function () 
            $(this).attr('name', $(this).attr('ci-name'));
        );
    );
);

第 1 步: 每当在中继器中使用单选按钮时,它的组名都会更改,因为 asp.net 会更改它以使其唯一。因此,每个单选按钮都有不同的组名(客户端生成的标记中的名称属性)。因此,用户可以同时选择所有选项。如后续 cmets 所解释的,此问题通过使用 jquery 代码得到解决。

第 2 步: 此代码块创建一个名为 ci-name 的新客户属性,并将 name 属性的原始值复制到这个新的自定义属性中。这个过程对轮询中的每个单选按钮重复。这一步对我们后面的步骤有帮助。

第 3 步: 此代码块将 poll 中所有单选按钮的名称属性值设置为第一个单选按钮的名称属性值。此步骤可防止用户一次选择多个选项。

第 4 步: 此代码在投票按钮单击事件的事件处理程序中,检查用户是否至少检查了一个选项。如果他没有,则会显示一条错误消息。

第 5 步: 此代码在投票按钮单击事件的事件处理程序中,将所有单选按钮的名称属性值设置为其原始值。这是通过从自定义属性 ci-name 复制值来实现的。这允许 asp.net 服务器端代码知道实际单击了哪个按钮。

【讨论】:

【参考方案10】:

我也对这个错误感到困惑,并决定放弃转发器,转而动态地构建一个包含控件的表格。在您的用户控件或页面上,只需添加以下元素:

<asp:Table ID="theTable" runat="server">
    <asp:TableHeaderRow runat="server">
        <asp:TableHeaderCell runat="server" Text="Column 1"/>
        <asp:TableHeaderCell runat="server" Text="Column 2"/>
        <asp:TableHeaderCell runat="server" Text="Column 3"/>
    </asp:TableHeaderRow>
</asp:Table>

然后在代码后面添加单选按钮和其他所需控件的数据行。您当然可以对其他元素(如 DIV)执行相同操作:

<div runat="server" ID=theDiv">
</div>

但让我们仍然希望 ASP.NET 团队能够解决这个与转发器和列表视图有关的不幸问题。我仍然喜欢中继器控件并尽可能使用它。

【讨论】:

【参考方案11】:

这是一种使用反射的纯服务器端方法。 RadioButton 控件使用UniqueGroupName 属性来确定组名。组名缓存在_uniqueGroupName 字段中。通过使用反射设置此字段,我们可以覆盖默认组名,并在转发器中的所有单选按钮中使用相同的组名。请注意,此代码必须在“RadioButton”控件的“PreRender”事件中运行,以确保新组名在回发中保持不变。

protected void rbOption_PreRender(object sender, EventArgs e)

    // Get the radio button.
    RadioButton rbOption = (RadioButton) sender;

    // Set the group name.
    var groupNameField = typeof(RadioButton).GetField("_uniqueGroupName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    groupNameField.SetValue(rbOption, "MyGroupName");

    // Set the radio button to checked if it was selected at the last post back.
    var groupValue = Request.Form["MyGroupName"];

    if(rbOption.Attributes["value"] == groupValue)
    
        rbOption.Checked = true;
     

RadioButton源代码:http://reflector.webtropy.com/default.aspx/Net/Net/3@5@50727@3053/DEVDIV/depot/DevDiv/releases/whidbey/netfxsp/ndp/fx/src/xsp/System/Web/UI/WebControls/RadioButton@cs/2/RadioButton@cs

【讨论】:

我是否正确,这样做会阻止 ASP.NET 观察 RadioButton 的 Checked 状态,并且您需要手动检查 Request.Form 的状态? 而且它还阻止其 OnCheckedChanged 事件处理程序触发?【参考方案12】:

这可能不是每个人的理想解决方案,但我使用 jQuery 完成了以下操作。

&lt;asp:RadioButton ID="rbtnButton1" groupName="Group1" runat="server" /&gt; &lt;asp:RadioButton ID="rbtnButton2" groupName="Group1" runat="server" /&gt;

etc...

然后在您的母版页中包含以下代码。 (或您的所有页面)

$(function() 
//This is a workaround for Microsoft's GroupName bug.  
//Set the radio button's groupName attribute to make it work.
$('span[groupName] > input').click(function() 
    var element = this;
    var span = $(element).parent();
    if (element.checked) 
        var groupName = $(span).attr('groupName');
        var inputs = $('span[groupName=' + groupName + '] > input')
        inputs.each(function() 
            if (element != this)
                this.checked = false;
        );
    
);

);

【讨论】:

【参考方案13】:

通过忽略RenderedNameAttribute 属性来解决HtmlInputControl.RenderAttributes() 方法的自定义控件/覆盖:

/// <summary>
/// HACK:  For Microsoft&apos;s bug whereby they mash-up value of the "name" attribute.
/// </summary>
public class NameFixHtmlInputRadioButton : HtmlInputRadioButton

    protected override void RenderAttributes(HtmlTextWriter writer)
    
        // BUG:  Usage of 'HtmlInputControl.RenderedNameAttribute'
        // writer.WriteAttribute("name", this.RenderedNameAttribute);
        writer.WriteAttribute(@"name", Attributes[@"Name"]);

        Attributes.Remove(@"name");

        var flag = false;
        var type = Type;

        if (! string.IsNullOrEmpty(type))
        
            writer.WriteAttribute(@"type", type);
            Attributes.Remove(@"type");
            flag = true;
        

        base.RenderAttributes(writer);

        if (flag && DesignMode)
        
            Attributes.Add(@"type", type);
        

        writer.Write(@" /");
    

【讨论】:

【参考方案14】:

我使用相同的技术取消选中其他收音机与 jquery 请找到下面的代码

<asp:RadioButton ID="rbSelectShiptoShop" runat="server" onchange="UnCheckRadio(this);" CssClass="radioShop" />

和下面的脚本

function UnCheckRadio(obj) 
           $('.radioShop input[type="radio"]').attr('checked', false);
           $(obj).children().attr('checked', true);
       

【讨论】:

以上是关于ASP.NET RadioButton 与名称(组名)混淆的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET C#中Radiobutton控件的疑问

ASP.NET中RadioButton与Button的使用

asp.net(C#)怎样将listview中的radiobutton设置成互斥?

ASP.NET GridView中加入RadioButton不能单选的解决方案

asp.net中radiobutton 的问题 C#

ASP.NET C#中Radiobutton控件的疑问