I was recently asked a question about the ASP.NET Repeater control and I thought it would make a good article. One of my colleagues had a repeater control, and in each row they had a radio button, along with several columns of data to display to the user. One of the columns of data was a unique identifier that identified which customer the user was browsing. My colleague couldn’t work out how to find the unique identifier for the selected customer. I said use LINQ! To demonstrate this I’m going to create some fake data to show you how to handle this scenario.
To begin with open Visual Studio 2008 and choose File > New > Web > ASP.NET Web Application. The first thing to do is to create a class that will be the structure of our data. Add a new class to the project and name it People. Add the following code to the People class:
C#
public class People
{
public int UniqueId { get; set; }
public string GivenName { get; set; }
public string Surname { get; set; }
public int Height { get; set; }
public int ShoeSize { get; set; }
public int Age { get; set; }
}
VB.NET
Public Class People
Private privateUniqueId As Integer
Public Property UniqueId() As Integer
Get
Return privateUniqueId
End Get
Set(ByVal value As Integer)
privateUniqueId = value
End Set
End Property
Private privateGivenName As String
Public Property GivenName() As String
Get
Return privateGivenName
End Get
Set(ByVal value As String)
privateGivenName = value
End Set
End Property
Private privateSurname As String
Public Property Surname() As String
Get
Return privateSurname
End Get
Set(ByVal value As String)
privateSurname = value
End Set
End Property
Private privateHeight As Integer
Public Property Height() As Integer
Get
Return privateHeight
End Get
Set(ByVal value As Integer)
privateHeight = value
End Set
End Property
Private privateShoeSize As Integer
Public Property ShoeSize() As Integer
Get
Return privateShoeSize
End Get
Set(ByVal value As Integer)
privateShoeSize = value
End Set
End Property
Private privateAge As Integer
Public Property Age() As Integer
Get
Return privateAge
End Get
Set(ByVal value As Integer)
privateAge = value
End Set
End Property
End Class
The UniqueId property will be the unique identifier for the user, so naturally we do not want to show this to the user. The next step is to add a repeater control to the default.aspx page. Add the following code:
<asp:Repeater ID="rptPeople" runat="server"
OnItemDataBound="rptPeople_ItemDataBound">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:RadioButton ID="rdoSelected" GroupName="Person"
TextAlign="Right" runat="server"
Text='<%# Eval("GivenName") %>' />
<asp:HiddenField ID="hdnUniqueId" runat="server"
Value='<%# Eval("UniqueId") %>' />
</td>
<td>
<asp:Label ID="Label3" runat="server"
Text='<%# Eval("Surname") %>' />
</td>
<td>
<asp:Label ID="lblSurname" runat="server"
Text='<%# Eval("Height") %>' />
</td>
<td>
<asp:Label ID="Label1" runat="server"
Text='<%# Eval("ShoeSize") %>' />
</td>
<td>
<asp:Label ID="Label2" runat="server"
Text='<%# Eval("Age") %>' />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
<asp:Button ID="btnSubmit" runat="server"
Text="Submit" OnClick="Button1_Click" />
If you ran the code right now, the user would be able to select every radio button, even though I have specified a GroupName. This is because the repeater control mangles the GroupName when it is rendered to the browser. The workaround for this is the following JavaScript I found here:
<script language="javascript" type="text/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;
}
</script>
This code needs to be set when an item is bound to the repeater. Add the following ItemDataBound code:
C#
protected void rptPeople_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType !=ListItemType.AlternatingItem)
{
return;
}
RadioButton rdo = e.Item.FindControl("rdoSelected") as RadioButton;
string script = "SetUniqueRadioButton('rptPeople.*Person',this)";
rdo.Attributes.Add("onclick", script);
}
VB.NET
Protected Sub rptPeople_ItemDataBound(ByVal sender As Object, ByVal e AsRepeaterItemEventArgs)
If e.Item.ItemType <> ListItemType.Item AndAlso e.Item.ItemType <> ListItemType.AlternatingItem Then
Return
End If
Dim rdo As RadioButton = TryCast(e.Item.FindControl("rdoSelected"), RadioButton)
Dim script As String = "SetUniqueRadioButton('rptPeople.*Person',this)"
rdo.Attributes.Add("onclick", script)
End Sub
That will ensure the user can only select one radio button at a time, which is normal functionality. To add data to the people class, add the following code to the page load event:
C#
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
List<People> people = new List<People>();
people.Add(new People()
{
GivenName = "Malcolm",
Surname = "Sheridan",
Age = 32,
Height = 185,
ShoeSize = 13,
UniqueId = 1
});
people.Add(new People()
{
GivenName = "Suprotim",
Surname = "Argawal",
Age = 28,
Height = 175,
ShoeSize = 10,
UniqueId = 2
});
rptPeople.DataSource = people;
rptPeople.DataBind();
}
}
VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
If (Not IsPostBack) Then
Dim people As New List(Of People)()
people.Add(New People() With {.GivenName = "Malcolm", .Surname = "Sheridan", .Age = 32, .Height = 185, .ShoeSize = 13, .UniqueId = 1})
people.Add(New People() With {.GivenName = "Suprotim", .Surname = "Argawal", .Age = 28, .Height = 175, .ShoeSize = 10, .UniqueId = 2})
rptPeople.DataSource = people
rptPeople.DataBind()
End If
End Sub
The last thing to do is to find out which row the user has selected. For this task I’m going to use LINQ. Add the following code to find the selected user:
C#
protected void Button1_Click(object sender, EventArgs e)
{
var query = (from p in rptPeople.Items.OfType<RepeaterItem>()
let o = p.Controls[1] as RadioButton
let hid = p.Controls[3] as HiddenField
where o.Checked
select new
{
GivenName = o.Text,
UniqueId = hid.Value
}).Single();
Response.Write(query.UniqueId);
}
VB.NET
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim query = ( _
From p In rptPeople.Items.OfType(Of RepeaterItem)() _
Let o = TryCast(p.Controls(1), RadioButton) _
Let hid = TryCast(p.Controls(3), HiddenField) _
Where o.Checked _
Select New With {Key .GivenName = o.Text, Key .UniqueId = hid.Value}).Single()
Response.Write(query.UniqueId)
End Sub
To see which radio button is selected, I am using the let keyword. This creates a temporary variable only available to the LINQ query. I also have another local variable that finds the UniqueId, which assigns the value to the hid variable. Finally the Single method is called to return the single record. If you run the project now and select a customer, the UniqueId will be displayed:
No comments:
Post a Comment