>Notice that our previously code-cluttered ItemTemplate is now considerably cleaner - this is because we''''ve pushed the logic to the itemDataBoundRepeater_ItemDataBound function in codebehind:
1: protected void itemDataBoundRepeater_ItemDataBound(object source, RepeaterItemEventArgs e) {
2: if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item){
3: Literal lit = (Literal)e.Item.FindControl("see");
4: if (lit != null){
5: Owner owner = (Owner)e.Item.DataItem;
6: if (owner.Pets.Count == 0){
7: lit.Text = "no pets";
8: }else{
9: lit.Text = "see pets";
10: }
11: }
12: }
13: } Since we are dealing with repeaters, e.Item returns a reference to the current RepeaterItem. If this was a datalist, it would return a reference to a DataListItem, or a DataGridItem if it were a datagrid. For the most part however, all three provide the same capabilities. The first thing to do is check the ItemType and make sure we are currently dealing with an AlternateItem or an Item [2]. Next get a reference to our literal [3], this is an extremely powerful capability which allows us to really keep our UI clean. As we saw in a previous section, we can cast DataItem directly to the individual item being bound (in this case Owner, but again, if we bound to a dataset, it would be a DataRowView) [5]. Finally all the pieces are in place to apply our presentation logic [6-10].
An alternative to using e.Item.FindControl() is to refer to the controls by position via e.Item.Controls[INDEX]. While this may be considerably faster, it really makes the UI inflexible to basic changes (else you face constantly changing the code). Additionally, white spaces and newlines are actually controls. So in the above code, you''''d get:
1: e.Item.Controls[0] //"\r\n 1 - \r\n "
2: e.Item.Controls[1] //is the actual "see" literal Which is both an unexpected behaviour and one very hard to cleanly deal with.
When it comes to OnItemDataBound, the sky is the limit. Here we''''ve only shown a basic example of what can be done and though we will see other, more complex examples, we won''''t cover every possibility.
OnItemCreatedAnother useful event exposed by these controls is OnItemCreated. The key difference between the two is that OnItemDataBound only fires when the control is bound - that is when you are posting back and the control is recreated from the viewstate, OnItemDataBound doesn''''t fire. OnItemCreated on the other hand fires when a control is bound AS WELL AS when the control is recreated from the viewstate. The following example shows this subtle difference:
1: <asp:Repeater OnItemCreated="repeater_ItemCreated" OnItemDataBound="repeater_ItemDataBound" id="repeater" Runat="server">
2: <ItemTemplate>
3: <asp:Literal EnableViewState="False" ID="event" Runat="server" /> <br />
4: </ItemTemplate>
5: </asp:Repeater>
6:
7: <asp:Button ID="btn" Runat="server" Text="Click Me!" /> Here we have a repeater with both the OnItemCreated and OnItemDataBound events hooked [1]. Additionaly we have a single literal who''''s viewstate is disabled (if it was enabled we couldn''''t see the difference) [3]. And we have a button that''''ll do nothing but postback [7]. Our codebehind looks like:
1: private void Page_Load(object sender, EventArgs e) {
2: if (!Page.IsPostBack){
3: repeater.DataSource = CustomerUtility.GetAllOrders();
4: repeater.DataBind();
5: }
6: }
7: protected void repeater_ItemDataBound(object source, RepeaterItemEventArgs e) {
8: if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item){
9: Literal lit = (Literal)e.Item.FindControl("event");
10: if (lit != null){
11: lit.Text += " - ItemDataBound";
12: }
13: }
14: }
15: protected void repeater_ItemCreated(object source, RepeaterItemEventArgs e) {
16: if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item){
17: Literal lit = (Literal)e.Item.FindControl("event");
18: if (lit != null){
19: lit.Text += "ItemCreated";
20: }
21: }
22: } When the page is first loaded, Page.IsPostBack returns false [2] and our repeater is bound to all orders [3,4]. Calling DataBind() causes the ItemCreated event to fire for the first row, followed by the ItemDataBound event - in our example each will fire, one after the other, 11 times (since there are 11 orders). As we can see, ItemCreated and ItemDataBound merely take the literal and append the text "ItemCreated" and "ItemDataBound" respectively. The difference happens when our button is clicked. This causes Page_Load to fire, but this time Page.IsPostBack evaluates to true, thus skipping the binding [3,4]. Only when the page enters its Begin PreRender stage will the ItemCreated event fire (again once for each row), but this time it won''''t be followed by the ItemDataBound.
The really important thing to keep in mind is that when ItemCreated fires because of databinding, e.Item.DataItem will what you expect - a reference to the individual row being bound. However, when ItemCreated is fired from being re-created from the viewstate, e.Item.DataItem will be NULL. If you think about it this makes sense, the entire datasource isn''''t stored in the viewstate, only the individual controls and their values, as such its impossible to have access to the individual rows of data originally used when binding. Of course, this can lead to very buggy code. For example, if we took our previous ItemDataBound example and moved it to the ItemCreated event:
1: protected void itemCreatedRepeater_ItemCreatedobject source, RepeaterItemEventArgs e) {
2: if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item){
3: Literal lit = (Literal)e.Item.FindControl("see");
4: if (lit != null){
5: Owner owner = (Owner)e.Item.DataItem;
6: if (owner.Pets.Count == 0){
7: lit.Text = "no pets";
8: }else{
9: lit.Text = "see pets";
10: }
11: }
12: }
13: } When the page first loads, the above code will work fine. But if the page is postedback, e.Item.DataItem will be null, resulting in a runtime null reference error.
Nested BindingAnother common requirement is to nest controls within each other. Both of our sample data has a 1 to many relationship and are therefore ideal candidates. Our Customers dataset has a DataRelation set up between the Customer''''s customerId and the order''''s customerId:
1: ds.Relations.Add(new DataRelation("CustomerOrders", ds.Tables[0].Columns["CustomerId"], ds.Tables[1].Columns["CustomerId"])); And our Owner''''s have a Pets property which is a collection of all the pets they own.
The two ways that we''''ll look at nesting repeaters is via inline binding and using OnItemDataBound.
Inline
1: <asp:Repeater id="dataSetCasting" Runat="server">
2: <HeaderTemplate>
3: <ul>
4: </HeaderTemplate>
5: <ItemTemplate>
6: <li><%# ((DataRowView)Container.DataItem)["Name"]%>
7: <ul>
8: <asp:Repeater ID="orders" DataSource=''''<%# ((DataRowView)Container.DataItem).CreateChildView("CustomerOrders")%>'''' Runat="server">
9: <ItemTemplate>
10: <li><%# ((DataRowView)Container.DataItem)["Amount"]%></li>
11: </ItemTemplate>
12: </asp:Repeater>
13: </ul>
14: </li>
15: </ItemTemplate>
16: <FooterTemplate>
17: </ul>
18: </FooterTemplate>
19: </asp:Repeater> The important part being when we set the DataSource of our inner repeater [8]. The CreateChildView function our DataRowView is used, in conjuction with the name of our DataRelationship to return a DataView of all child records. Alternatively, using the DataBinder.Eval, we could simply use:
1: <asp:Repeater ID="orders" DataSource=''''<%# DataBinder.Eval(Container.DataItem, "CutomerOrders")%>'''' Runat="server"> Again, we use the CustomerOrders datarelation which we created, but let the DataBinder.Eval handle everything else.
Nesting with custom collections is even easier. Since owners have a property called Pets which is a custom collection of all the pets they own, we can simply:
1: <asp:Repeater id="collectionCasting" Runat="server">
2: <HeaderTemplate>
3: <ul>
4: </HeaderTemplate>
5: <ItemTemplate>
6: <li><%# ((Owner)Container.DataItem).FirstName%>
7: <ul>
8: <asp:Repeater ID="pets" DataSource="<%# ((Owner)Container.DataItem).Pets%>" Runat="server">
9: <ItemTemplate>
10: <li><%# ((Pet)Container.DataItem).Name%></li>
11: </ItemTemplate>
12: </asp:Repeater>
13: </ul>
14: </li>
15: </ItemTemplate>
16: <FooterTemplate>
17: </ul>
18: </FooterTemplate>
19: </asp:Repeater> Or using DataBinder.Eval:
1: <asp:Repeater ID="pets" DataSource=''''<%# DataBinder.Eval(Container.DataItem, "Pets")%>'''' Runat="server">
OnItemDataBoundIf something is doable using inline ASPX, it''''s doable via onItemDataBound. Deciding which method to use often depends on which you feel is cleaner and more flexible. We''''ll only look at one example, since it''''s basically the same as the above code, except the binding logic is moved to codebehind:
1: <asp:Repeater OnI上一页 [1] [2] [3] 下一页 [Web开发]一个关于ASP运行时间计算的代码 [Web开发]ASP:检测含有中文字符串的实际长度 [Web开发]asp 中英文字符长度检测判断函数 [Web开发]安全维护 IIS asp 站点的高级技巧 [Access]ASP&SQL让select查询结果随机排序的实现方法 [Web开发]ASP字符串截取函数 [Web开发][asp]关键词只替换一次的写法 [Web开发]Asp无组件生成缩略图方法详解 [Web开发]asp编程中优化数据库方法详解 [Web开发]三种方法教你asp如何去除html标记
|