Friday, May 13, 2005 8:50 AM
triplez
Accessing controls within asp:Repeater
Here's something I'd like to share with everyone. I'm not too sure whether there's a better way to do things, and I do really hope someone would suggest to me a better way to do it. :P
Ok! Today I'm going to talk about accessing your controls within the <ItemTemplate>. Here's the problem statement.
I use a repeater to display some stuff. For example,
<snip>
<asp:Repeater id="myRepeater" Runat="server">
<ItemTemplate>
<asp:DropDownList Runat="server" ID="myDropDownList" AutoPostBack="True" OnSelectedIndexChanged="myDropDownList_OnSelectedIndexChanged">
<asp:ListItem Value="Test 1">Test 1</asp:ListItem>
<asp:ListItem Value="Test 2">Test 2</asp:ListItem>
</asp:DropDownList>
<asp:RadioButtonList Runat="server" ID="myRadioButtonList" AutoPostBack="True" CssClass="NormalText12" Visible="False" OnSelectedIndexChanged="myRadioButtonList_OnSelectedIndexChanged">
<asp:ListItem Value="Test 3" Selected="True">Test 3</asp:ListItem>
<asp:ListItem Value="Test 4">Test 4</asp:ListItem>
</asp:RadioButtonList>
</ItemTemplate>
</asp:Repeater>
</snip>
Ok, Let me explain the senario. I have a DropDownList that's called myDropDownList. When the index is changed, it will trigger an event called myDropDownList_OnSelectedIndexChanged. Notice I didn't assign it in the C# code, but instead in the asp.net code. When this event is triggered, it will hide and unhide myRadioButtonList, depending on the requirement. But anyway, this is just an example. Sounds simple right?
Now the problem, because the Repeater will duplicate and repeat the asp.net code within <ItemTemplate>, this poses a problem with the identifier name, or Control.ID. Because all the ID will be the same. So the repeater has to change the ID to make it unique. This is how they do it. The repeater will change the ID to a format similar to this. (repeaterID)_(not too sure but it's usually blank)_ctl(item number)_(control id). So!! The ID of the myDropDownList for the first item will change to myRepeater__ctl1_myDropDownList, the ID for the 2nd myDropDownList will be myRepeater__ctl2_myDropDownList, and so on. Got my drift already?
Since the IDs change for each item, how do we get the instance of the control then?? Well, within the Repeater class, there's a property called Items which is a collection class that handles all your controls within each Item. So all the controls within the 1st Item will be accessed through myRepeater.Items[0].Controls["myDropDownList"];. And no, you can't just use the ID "myRepeater__ctl1_myDropDownList" to access it through myRepeater.Controls["myRepeater__ctl1_myDropDownList"].
Anyway, so now, I'd need to determine which row it's being triggered from. Coming back to the event, the same myDropDownList_OnSelectedIndexChanged event is triggered for EVERY row. Here's the definition for that event.
<snip>
public void myDropDownList_OnSelectedIndexChanged(object sender, System.EventArgs e)
</snip>
Notice it's a public method. That's because the controls need to trigger this event from OUTSIDE the page class. Remember, you can't really declare your controls within the page class itself, because of the conflicting IDs.
Coming back, here's what I did to get which row (or item) triggered this event. Simply this 2 lines of code.
<snip>
string[] atrTest = (Control)sender.ClientID.Split('_');
int ItemNumber = Convert.ToInt32(atrTest[2].Remove(0, 3)) - 1;
</snip>
Ok. Why use ClientID instead of ID? Because the ID will consist of myDropDownList, and not the entire string myRepeater__ctl1_myDropDownList. Let me just explain through the code. Basically, I split the entire string and it returns me the splitted string. atrTest[0] will contain "myRepeater", atrTest[1] will contain "", atrTest[2] will contain "ctl1", atrTest[3] will contain "myDropDownList". The next line of code is simply to remove the "ctl" from the string. And that's that. I got the row number. But notice I have to - 1. That is because we start from 0, but row number starts from 1.
Now that we've got the item number, we can now access the control by doing myRepeater.Items[ItemNumber].FindControl("myDropDownList"); But, there's actually a faster way to do it than using a FindControl. FindControl is known to be a big performance hit. You can do a foreach for the Controls collection and do a condition to check the type instead. That is much faster.
Well, now the big problem. Is there a better solution than this? Or a more elegant one? :) Someone, anyone! :P
Filed under: Programming