问题
I have the following nested listview...
<asp:ListView ID="lvwRiskQuestions" runat="server" ItemPlaceholderID="QuestionItemPlaceholder">
<LayoutTemplate>
<asp:PlaceHolder ID="QuestionItemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<%# Eval("DESCRIPTION")%>
<asp:ListView ID="lvwAnswers" runat="server" ItemPlaceholderID="AnswerItemPlaceholder" DataSource='<%# Eval("Answers")%>'>
<LayoutTemplate>
<asp:PlaceHolder ID="AnswerItemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<asp:RadioButton ID="rdbSelect" runat="server" AutoPostBack="true" OnCheckedChanged="rdbSelectChanged"/>
<%# Eval("Description")%>
</ItemTemplate>
</asp:ListView>
</ItemTemplate>
</asp:ListView>
I get hold of the radio buttons OnCheckedChanged like so...
Protected Sub rdbSelectChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Dim rb1 As RadioButton = CType(sender, RadioButton)
Dim lvwAnswers = DirectCast(lvwRiskQuestions.FindControl("lvwAnswers"), ListView)
For Each row As ListViewItem In lvwAnswers.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
The problem i have is 'lvwAnswers' is Nothing. I'm guessing im not doing my findcontrol correctly.
Any help greatly appreciated.
回答1:
If you're just generating a list of radio-buttons for the answers, you could use the RadioButtonList control. This would generate the correct HTML so that only one answer could be selected per question without having to post-back to de-select the other options.
If your answer template contains more than a single RadioButton
, things get more complicated. When it's not hosted in a RadioButtonList
, the RadioButton
uses the UniqueID
of the parent NamingContainer
to build its unique group name. Unfortunately, in your example, the NamingContainer
will be the ListViewDataItem
from the lvwAnswers list, and each answer will have a different ID.
What you need is a RadioButton
which will look at the NamingContainer
's NamingContainer
to generate its group name. You could either re-implement the RadioButton
control, or use a little bit of reflection to update the private _uniqueGroupName
field:
[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);
}
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);
}
}
With that control in place and registered using the site
prefix, you can change your code to:
<asp:ListView ID="lvwRiskQuestions" runat="server" ItemPlaceholderID="QuestionItemPlaceholder">
<LayoutTemplate>
<asp:PlaceHolder ID="QuestionItemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<%# Eval("DESCRIPTION") %>
<asp:ListView ID="lvwAnswers" runat="server" ItemPlaceholderID="AnswerItemPlaceholder" DataSource='<%# Eval("Answers")%>'>
<LayoutTemplate>
<asp:PlaceHolder ID="AnswerItemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<site:ListRadioButton ID="rdbSelect" runat="server"
Text='<%# Eval("Description") %>'
/>
</ItemTemplate>
</asp:ListView>
</ItemTemplate>
</asp:ListView>
In the rendered HTML, the radio-buttons for each question will then have the same name
, and you will only be able to select a single answer per question, without having to post the entire page on each selection.
回答2:
I'd like to point out that this "copy/paste" code doesn't work and was taken from a comment on codeproject (Comment titled Another Option). The original code does work.
Here it is :
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class SimpleRadioButton : RadioButton
{
private static readonly FieldInfo UniqueGroupNameField = FindUniqueGroupNameField();
private string _uniqueGroupName;
private static FieldInfo FindUniqueGroupNameField()
{
return typeof(RadioButton).GetField("_uniqueGroupName",
BindingFlags.NonPublic | BindingFlags.Instance);
}
protected virtual string CreateUniqueGroupName()
{
string result = this.GroupName;
if (string.IsNullOrEmpty(result))
{
result = this.ID;
}
if (string.IsNullOrEmpty(result))
{
result = this.UniqueID;
}
else
{
Control container = this.NamingContainer;
if (null != container)
{
if (container is IDataItemContainer)
{
container = container.NamingContainer ?? container;
}
result = container.UniqueID + base.IdSeparator + result;
}
else
{
string uniqueID = this.UniqueID;
if (!string.IsNullOrEmpty(uniqueID))
{
int index = uniqueID.LastIndexOf(base.IdSeparator);
if (-1 != index)
{
result = uniqueID.Substring(0, 1 + index) + result;
}
}
}
}
return result;
}
private void EnsureUniqueGroupName()
{
if (null == _uniqueGroupName)
{
string value = this.CreateUniqueGroupName();
if (null != UniqueGroupNameField) UniqueGroupNameField.SetValue(this, value);
_uniqueGroupName = value;
// Make sure we have a value attribute:
value = base.Attributes["value"];
if (string.IsNullOrEmpty(value))
{
base.Attributes["value"] = this.UniqueID;
}
}
}
protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
this.EnsureUniqueGroupName();
return base.LoadPostData(postDataKey, postCollection);
}
protected override void Render(HtmlTextWriter writer)
{
this.EnsureUniqueGroupName();
base.Render(writer);
}
}
来源:https://stackoverflow.com/questions/13269773/oncheckedchanged-nested-listview-controls