Wednesday 20 April 2011

How to edit nested ListView in ASP.NET

ListView is a cool control in ASP.NET. It is usefull for - well - listing data.

Even cooler you can nest a ListView inside another ListView if you need to group your list into groups - e.g. log grouped by date or an OrderHeader -> OrderLines grouping - and it is easy.

Unfortunately editing nested items is a bit clumcy - and it took me some time to figure it out. I have made an example here with two nested ListViews.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="NesterListTest.WebForm1" EnableEventValidation="false" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="
http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

        <asp:ListView ID="ListView1" runat="server" DataKeyNames="Id" OnItemDataBound="BindNestedList">
        <LayoutTemplate>
            <table id="RegistrationLines">
                <tr id="itemPlaceholder" runat="server"></tr>
            </table>
        </LayoutTemplate>
        <ItemTemplate>
            <tr><th>OuterIndex: <%# Eval("Id") %></th></tr>
           
            <asp:ListView ID="ListView2" runat="server" DataKeyNames="Id" OnItemEditing="EditNestedItem">
            <ItemTemplate>
                <tr>
                    <td><%# Eval("Value") %></td>
                    <td><asp:Button runat="server" ID="EditButton" CommandName="edit" Text="Edit" /></td>
                </tr>
            </ItemTemplate>
            <EditItemTemplate>
                <tr>
                    <td>Now editing -></td>
                    <td><%# Eval("Value") %></td>
                </tr>
            </EditItemTemplate>
            </asp:ListView>
        </ItemTemplate>
        </asp:ListView>  
    </div>
    </form>
</body>
</html>


In the Page_Load I just create some funke sample data. Note that I have two classes. These are just for holding the data - nothing else.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace NesterListTest
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        List<TestContainer> list;

        protected void Page_Load(object sender, EventArgs e)
        {
            list = new List<TestContainer>();

            for (int i = 0; i < 5; i++)
            {
                TestContainer tc = new TestContainer() { Id = i };

                List<TestItem> l = new List<TestItem>();
                for (int j = 0; j < i % 3; j++)
                {
                    TestItem t = new TestItem() { Id = j, Value = "Cheese " + i + " > " + j };
                    l.Add(t);
                }
                tc.Items = l.ToArray();
                list.Add(tc);
            }

            ListView1.DataSource = list;
            ListView1.DataBind();
        }

        protected void BindNestedList(Object sender, ListViewItemEventArgs e)
        {
            if (e.Item.ItemType == ListViewItemType.DataItem)
            {
                ListViewDataItem item = (ListViewDataItem)e.Item;
                TestContainer container = (TestContainer)item.DataItem;
                ListView innerList = (ListView)item.FindControl("ListView2");
                innerList.DataSource = container.Items;
                innerList.DataBind();
            }
        }

        protected void EditNestedItem(Object sender, ListViewEditEventArgs e)
        {
            ListView lvs = sender as ListView;

            ListViewDataItem ctl = (ListViewDataItem)ListView1.FindControl(lvs.Parent.ID);
            if (ctl != null)
            {
                ListView lv = (ListView)ctl.FindControl("ListView2");
                lv.EditIndex = e.NewEditIndex;

                int indexOfParentControl = ListView1.Items.IndexOf(ctl);
                TestContainer t = list[indexOfParentControl];
                lv.DataSource = t.Items;
                lv.DataBind();
            }
        }
    }

    public class TestContainer
    {
        public int Id { get; set; }
        public TestItem[] Items { get; set; }
    }

    public class TestItem
    {
        public int Id { get; set; }
        public string Value { get; set; }
    }
}



In the EditNestedItem method, I find the parent control of the sender. As the sender is the inner ListView, the parent of this is a ListViewDataItem of the outer ListView. In the ListViewDataItem I find the inner ListView and set the EditIndex of it.
I then find the index of the ListViewDataItem in the outer ListViews items collection and now I have the index of the TestContainer for ListViewDataItem. Then I can grap it from the list and bind the Items of the TestContainer to the inner ListView.

3 comments:

Anonymous said...

Hi,

I was reading your article and I would like to appreciate you for making it very simple and understandable. This article gives me a basic idea of Nested Listview in Asp.Net and it helped me a lot. Thanks for sharing with us. I had found another nice post with wonderful explanation on Asp.Net Nested ListeView, for more details of that post link, check out this link......
Nested ListView in Asp.Net

Thank you very much!

Anonymous said...

it was helpful

Anonymous said...

What about if I have a button in the nested listview item template, how would I program a value to it on databound, and detect a click event that passes that value?