Modifying Data with the ListView's EditItemTemplate
I am lovin' the total control the ListView control is giving me. This past week a link to Telerik's new RadGrid control was floating around the office - and the general consensus was that the in-line editing capabilities control was pretty cool. And I completely agree - the in-line edit feature is pretty useful. But ... you don't need to buy the RadGrid to create a grid like this. ASP.NET 3.5's ListView control provides excellent out of the box support for scenarios like this.
Check out the screen shot below (or even better, open up my live demo). This grid provides the same in-line editing support as the RadGrid, it is incredibly flexible if you want to tweak the layout, and best of all there is absolutely zero codebehind logic! Did I mention I am lovin' the new ListView?
Below are some of the implementation details - read on if you are interested.
Live Demo (IE6, IE7, FF, Opera) | Download
Creating the ListView - Defining the Template Shells
The first thing I like to do when using the ListView is to stub out the control and fill in the templates that I plan on using with a few comments regarding what bit of HTML these templates will be responsible for rendering. For this example, I know that I will need to make use of three templates: the LayoutTemplate for setting up the overall structure, an ItemTemplate for defining the read-only item template and finally an EditItemTemplate that contains the template for the item that is currently being edited.
1: <asp:ListView ID="ListView" runat="server">
2: <LayoutTemplate>
3: <!--
4: Define the core structure for my grid
5: for this example this will be a TABLE element
6: -->
7: </LayoutTemplate>
8: <ItemTemplate>
9: <!--
10: Define the bit of HTML to render for each object in my datasource
11: for this example, this will be a TR element
12: -->
13: </ItemTemplate>
14: <EditItemTemplate>
15: <!--
16: Define the core structre for my grid
17: for this example, this will be 2 TR's,
18: one that displays the header information and
19: another that contains a nested TABLE that contains
20: the INPUT elements
21: -->
22: </EditItemTemplate>
23: </asp:ListView>
Creating the ListView - Filling in LayoutTemplate
I want my grid to render as an HTML table. So I use the LayoutTemplate to define the grids column header values as well as create the placeholder element for my other templates.
1: <asp:ListView ID="ListView" runat="server">
2: <LayoutTemplate>
3: <table class="gridview" cellpadding="0" cellspacing="0">
4: <tr class="header">
5: <th></th>
6: <th>ID</th>
7: <th>Name</th>
8: <th>Address</th>
9: <th>Zip Code</th>
10: <th>Phone</th>
11: </tr>
12: <tr id="itemPlaceholder" runat="server" />
13: </table>
14: </LayoutTemplate>
15: <ItemTemplate />
16: <EditItemTemplate />
17: </asp:ListView>
Creating the ListView - Filling in the ItemTemplate
And below is the markup I am using for my ItemTemplate. Nothing too magical here, just specifing that each object in my datasource will be represented within the TABLE as a row. There are 3 items here to take note of ...
- The ListView does define an AlternatingItemTemplate, but I personally don't find it really all that useful. The only variation I usually want between my ItemTemplate and the AlternatingItemTemplate is the CSS class that is applied to the row - and there is no need to redefine your complete ItemTemplate to achieve this. Instead you can dynamically set the CSS class value of the TR element using the following expression:
<%# Container.DataItemIndex % 2 == 0 ? "row" : "row alt" %>
Which will render <tr class="row" /> for even rows and <tr class="row alt" /> for odd ones
- I have include a LinkButton to the template and set the CommandName property to Edit. This will automatically (i.e. no extra code required) cause a postback and lets the ListView know that it needs to render the EditItemTemplate for the data item that is bound to the current row.
- Notice that I am using the ?? operator to explicitly set the value of the cell to a non-breaking space if the property value is null. This avoids the weirdness of not having styles applied to empty cells (i.e. <TD></TD>).
1: <asp:ListView ID="ListView" runat="server">
2: <LayoutTemplate />
3: <ItemTemplate>
4: <tr class='<%# Container.DataItemIndex % 2 == 0 ? "row" : "row alt" %>'>
5: <td class="command">
6: <asp:LinkButton
7: ID="btnEdit" runat="server"
8: Text="Edit" CommandName="Edit"
9: />
10: </td>
11: <td><%# Eval("ID") %></td>
12: <td><%# Eval("ContactName") ?? "nbsp;" %></td>
13: <td><%# Eval("Address") ?? "nbsp;" %></td>
14: <td><%# Eval("ZIPCode") ?? "nbsp;" %></td>
15: <td><%# Eval("Phone") ?? "nbsp;" %></td>
16: </tr>
17: </ItemTemplate>
18: <EditItemTemplate />
19: </asp:ListView>
Creating the ListView - Filling in the EditItemTemplate
When an item is being edited, I want to render 2 TR's. The first one displays what was in the original ItemTemplate, except the data values are in a bold font. In the second TR I want to render a nested 3x3 TABLE that displays the additional attributes of each item that can be edited. Besides these input elements, I also want to allow the user to save or cancel their changes. Below is a sample screen shot ...
Below is the markup for my EditItemTemplate. Again, there are a few things to notice ...
- Like I mentioned before, I am actually rendering 2 TR's for the same data item. The first one is virtually identical to the one the ItemTemplate is configured to render as well. The only different is that I have applied a custom CSS class to this row called edit-info that allows me to use CSS to control some of the display attributes (like setting the font-weight to bold).
- I am also using an expression to dynamically add the first class to the row as well when it is the first data bound item. This allows me to not apply the dashed border when the first row in the grid is being edited (this didn't look very good).
- I have included LinkButtons that have the CommandName attribute's set to Save and Cancel that tell the ListView when to save or abort the changes. When the Save button is clicked the ListView coordinates with the datasource to get the changes persisted (for this demo, the changes are in-memory only)
1: <asp:ListView ID="ListView" runat="server">
2: <LayoutTemplate />
3: <ItemTemplate />
4: <EditItemTemplate>
5: <tr class='edit-info <%# Container.DataItemIndex == 0 ? "first" : string.Empty %>'>
6: <td class="command">
7: <asp:LinkButton ID="btnEdit" runat="server" Text="Edit" CommandName="Edit" />
8: </td>
9: <td><%# Eval("ID") %></td>
10: <td><%# Eval("ContactName") ?? "nbsp;" %></td>
11: <td><%# Eval("Address") ?? "nbsp;" %></td>
12: <td><%# Eval("ZIPCode") ?? "nbsp;" %></td>
13: <td><%# Eval("Phone") ?? "nbsp;" %></td>
14: </tr>
15: <tr>
16: <td class="edit" colspan="6">
17: <div class="details">
18: <div class="header">Edit details for '<%# Eval("ContactName")%>'</div>
19: <table class="detailview" cellpadding="0" cellspacing="0">
20: <tr>
21: <th>ID</th>
22: <td>
23: <asp:Label
24: ID="lblID" runat="server"
25: Text='<%# Bind("ID") %>' />
26: </td>
27: <th>Name</th>
28: <td>
29: <asp:TextBox
30: ID="txtName" runat="server"
31: Text='<%# Bind("ContactName") %>' />
32: </td>
33: <th>Title</th>
34: <td>
35: <asp:TextBox
36: ID="txtTitle" runat="server"
37: Text='<%# Bind("ContactTitle") %>' />
38: </td>
39: </tr>
40: <tr>
41: <th>Address</th>
42: <td>
43: <asp:TextBox
44: ID="txtAddress" runat="server"
45: Text='<%# Bind("Address") %>' />
46: </td>
47: <th>City</th>
48: <td>
49: <asp:TextBox
50: ID="txtCity" runat="server"
51: Text='<%# Bind("City") %>' />
52: </td>
53: <th>State</th>
54: <td>
55: <asp:TextBox
56: ID="txtState" runat="server"
57: Text='<%# Bind("State") %>' />
58: </td>
59: </tr>
60: <tr>
61: <th>ZIP Code</th>
62: <td>
63: <asp:TextBox
64: ID="txtZIPCode" runat="server"
65: Text='<%# Bind("ZIPCode") %>' />
66: </td>
67: <th>Phone</th>
68: <td>
69: <asp:TextBox
70: ID="txtPhone" runat="server"
71: Text='<%# Bind("Phone") %>' />
72: </td>
73: <th>Company</th>
74: <td>
75: <asp:TextBox
76: ID="txtCompany" runat="server"
77: Text='<%# Bind("CompanyName") %>' />
78: </td>
79: </tr>
80: </table>
81: <div class="footer command">
82: <asp:LinkButton
83: ID="btnSave" runat="server"
84: Text="Save" CommandName="Update"
85: />
86: <asp:LinkButton
87: ID="btnCancel" runat="server"
88: Text="Cancel" CommandName="Cancel"
89: />
90: </div>
91: </div>
92: </td>
93: </tr>
94: </EditItemTemplate>
95: </asp:ListView>
That's it. Enjoy!
Comments
You have learned me alot about web development!
Thank you for making ready to use,good looking and understandable examples :)
Keep up the good work!
Hi Matt,
You know all these great examples you keep posting, they could really make up an entire book (if not two or more). A kind of "cookbook", think about it? Cause I love your articles and I'm sure I'm not the only one. Your blog is a superb resource for ASP.NET developers, thank you and keep it up!
Matt,
You might want to take the edit button out of the EditItemTemplate so as not to confuse users.
Thank you again for all your work and wiliness to share. Yours is the first feed I look at to see if there is something new.
Bill
This is great! Keep it coming; Thanks
Wow. Looks great. Thanks for the great post again.
+1 Great stuff.
great article!加油, matt
Who needs RAD if you've got Matt?
I am really amazed with your articles.. Super work Matt!
I'm using the asp.net mvc:
I suspose in the edit template I can use my jquery ajax form post?
If listview is :
1. viewstate free &
2. postback free
then I would use it :)
Thanks for your continual great posts, I'd like to see some that work outside of just webforms?
Great stuff as always.
Hi,
It's nice to your Design and Edit opt .in the Gridview
Thanks
@Tor Christian - No problem, thanks for the comment.
@TimothyP - My thoughts exactly. Check out this out
@Bill - I played around with disabling it and even moving the Save/Close buttons to this location, but I thought leaving the Edit button the best option. I will try removing it all together and seeing how that looks.
Steve -
I hope to write a few posts on MVC soon ...
Great article!
Hi Matt,
this is a great site with very useful articles.
I have two questions regarding the listview control.
I've created a listview that loads an user control to its item template dynamically. Is it possible to load a user control to its layout template too? I was not able to do that. The second and more important issue is: I have a LinkButton in the user control that should fire a command event but it doesn't fire anywhere. Is this not possible?
Thank you.
ibram
If you want some fun, add the and try to add a dropdownlist and bind it to a LINQ query. Or, even try to populate the dropdownlist in the backend. I couldn't seem to get a handle on drop down list from of any of the existing events (except ones that occur after you click the insert button)
Matt great posting for a starter. Can you take it one more step further? Can you make one of the fields a drop down list with a "- Select One -" added as one of the items to choose from. Basically I would like to see a better real world example using drop downs in the edit and insert template. Most real world applications have relational data that the user choose from, can you add to the example? Of course I would want LINQ2SQL to be used for the population of the drop downs.
I did learn one thing on my search and that is:
you can setup your drop down like the following in order to add a "- Select One -" to the top of your databound list like the following:
Any help would be much appreciated on how to use events to set the default selected item for when editing and inserting. Thanks!
Hi I was wondering if you know why this is happening: I have a details page that contains a DetailsView control to display a record selected from a master page. Below the DetailsView I have a ListView control that grabs related content to the record displayed by the detaislView. Everything shows up properly, however, when I go to insert a record or page thru the records of the ListView, I loose the DetailsView all together, it disappears from the page. How can I have the DetailsView stay on the page as I interact with the ListView? Please advise.