|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Table of Contents
IntroductionQuestions regarding databinding, in one form or another, are probably the most asked in the ASP.NET newsgroups. It's clear everyone loves the idea of databinding but that more advanced functionality, such as event handling, conditional formatting and fine-tuning, aren't straightforward. The goal of this tutorial is to shed light on some of the more common and frequently asked questions about the capabilities of databinding. The Sample ProgramThroughout this tutorial, we'll use two separate data sources. The first will be your every-day Our
1A Our business entities will consist of an
Understanding DataItemYou've undoubtedly made frequent use of the 1: <%# DataBinder.Eval(Container.DataItem, "customerId") %>
It's important to understand that 1: <%@ Import namespace="System.Data" %>
2: <%@ Import namespace="BindingSample" %>
3: <asp:Repeater id="dataSetRepeater" Runat="server">
4: <ItemTemplate>
5: <%# ((DataRowView)Container.DataItem)["customerId"] %> -
6: <%# ((DataRowView)Container.DataItem)["Name"] %> <br />
7: </ItemTemplate>
8: <AlternatingItemTemplate>
9: <%# DataBinder.Eval(Container.DataItem, "customerId") %> -
10: <%# DataBinder.Eval(Container.DataItem, "Name") %> <br />
11: </AlternatingItemTemplate>
12: </asp:Repeater>
13:
14: <br><br>
15:
16: <asp:Repeater id="collectionRepeater" Runat="server">
17: <ItemTemplate>
18: <%# ((Owner)Container.DataItem).OwnerId %> -
19: <%# ((Owner)Container.DataItem).FirstName %> <br />
20: </ItemTemplate>
21: <AlternatingItemTemplate>
22: <%# DataBinder.Eval(Container.DataItem, "OwnerId") %> -
23: <%# DataBinder.Eval(Container.DataItem, "FirstName") %> <br />
24: </AlternatingItemTemplate>
25: </asp:Repeater>
In the first In the second In both cases, the FormattingInlineWhile binding, it's possible to do simple formatting directly in the databinding expression or by calling functions which reside in code-behind. 1: <asp:Repeater id="dataSetRepeater" Runat="server">
2: <ItemTemplate>
3: <%# DataBinder.Eval(Container.DataItem, "OrderId")%> -
4: <%# FormatDate(DataBinder.Eval(Container.DataItem, "Ordered"))%> -
5: <%# FormatMoney(DataBinder.Eval(Container.DataItem,
"Amount"))%> <br />
6: </ItemTemplate>
7: </asp:Repeater>
8:
9: <br ><br >
10:
11: <asp:Repeater id="collectionRepeater" Runat="server">
12: <ItemTemplate>
13: <%# DataBinder.Eval(Container.DataItem, "OwnerId") %> -
14: <asp:literal ID="see" Runat="server"
15: Visible='<%# (int)DataBinder.Eval(Container.DataItem,
"Pets.Count") > 0 %>'>
16: see pets
17: </asp:Literal>
18: <asp:literal ID="nopets" Runat="server"
19: Visible='<%# (int)DataBinder.Eval(Container.DataItem,
"Pets.Count") == 0 %>'>
20: no pets
21: </asp:Literal>
22: <br />
23: </ItemTemplate>
24: </asp:Repeater>
The second 1: protected string FormatDate(object date) {
2: if (date == DBNull.Value){
3: return "n/a";
4: }
5: try{
6: return ((DateTime)date).ToShortDateString();
7: }catch{
8: return "n/a";
9: }
10: }
11: protected string FormatMoney(object amount) {
12: if (amount == DBNull.Value){
13: return String.Format("{0:C}", 0);
14: }
15: return String.Format("{0:C}", amount);
16: }
OnItemDataBoundWhile the above method is suitable for quick and simple problems, it lacks in elegance and capacity. Indeed, the 2nd example shows a serious lack of grace, and dangerously blends presentation logic with UI. Avoiding burdening your presentation layer with any code is a practice worth eternal vigilance. To help accomplish this, the
Using 1: <asp:Repeater OnItemDataBound="itemDataBoundRepeater_ItemDataBound"
id="itemDataBoundRepeater" Runat="server">
2: <ItemTemplate>
3: <%# DataBinder.Eval(Container.DataItem, "OwnerId") %> -
4: <asp:Literal ID="see" Runat="server" /> <br />
5: </ItemTemplate>
6: </asp:Repeater>
Notice that our previously code-cluttered 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 An alternative to using 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 behavior and one very hard to cleanly deal with. When it comes to OnItemCreatedAnother useful event exposed by these controls is 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 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, The really important thing to keep in mind is that when 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 posted back, Nested BindingAnother common requirement is to nest controls within each other. Both of our sample data has a one to many relationship and are therefore ideal candidates. Our 1: ds.Relations.Add(new DataRelation("CustomerOrders",
ds.Tables[0].Columns["CustomerId"],
ds.Tables[1].Columns["CustomerId"]));
And our The two ways that we'll look at nesting 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 1: <asp:Repeater ID="orders"
DataSource='<%# DataBinder.Eval(Container.DataItem, "CutomerOrders")%>'
Runat="server">
Again, we use the Nesting with custom collections is even easier. Since 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 1: <asp:Repeater ID="pets"
DataSource='<%# DataBinder.Eval(Container.DataItem, "Pets")%>'
Runat="server">
OnItemDataBoundIf something is doable using inline ASPX, it's doable via 1: <asp:Repeater OnItemDataBound="dataSetCasting_ItemDataBound"
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" 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>
Notice that our inner 1: protected void dataSetCasting_ItemDataBound(object s,
RepeaterItemEventArgs e) {
2: if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType
== ListItemType.AlternatingItem){
3: Repeater rpt = (Repeater)e.Item.FindControl("orders");
4: if (rpt != null){
5: rpt.DataSource =
((DataRowView)e.Item.DataItem).CreateChildView("CustomerOrders");
6: rpt.DataBind();
7: }
8: }
9: }
Basically, the same thing is happening as we saw before, except this is happening out of the UI. Handling EventsThe last thing to discuss is how to handle events raised by controls inside your 1: <asp:Repeater OnItemCommand="eventRepeater_ItemCommand"
id="eventRepeater" Runat="server">
2: <ItemTemplate>
3: <%# DataBinder.Eval(Container.DataItem, "Name")%>
4: <asp:LinkButton ID="delete"
5: Runat="server"
6: CommandName="Delete"
7: CommandArgument='<%# DataBinder.Eval(Container.DataItem,
"CustomerId") %>'>
8: Delete Customer
9: </asp:LinkButton>
10: -
11: <asp:LinkButton ID="addOrder"
12: Runat="server"
13: CommandName="Add"
14: CommandArgument='<%# DataBinder.Eval(Container.DataItem,
"CustomerId") %>'>
15: Add Order
16: </asp:LinkButton>
17: <br />
18: </ItemTemplate>
19: </asp:Repeater>
In the above code, two 1: protected void eventRepeater_ItemCommand(object s,
RepeaterCommandEventArgs e) {
2: int customerId = Convert.ToInt32(e.CommandArgument);
3: switch (e.CommandName.ToUpper()){
4: case "DELETE":
5: CustomerUtility.DeleteCustomer(customerId);
6: BindEventRepeater(false);
7: break;
8: case "Add":
9: //doesn't actually do antyhing right now.
10: break;
11: }
12: }
Depending on what the DownloadThis sample web application simply contains a number of pages which do various things with
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||