In my previous post, I have shown the data binding capabilities of ListView control. In this post, I will show the Data Editing power of ListView. Like the previous DataGrid, DataList and GridView controls the ListView control has the EditItemTemplate and it also comes up with the long awaited InsertItemTemplate, in the past there was a lot of attempt to allow the users to insert data through Header or Footer Template which is no longer required. In this post I will show you the full data editing capabilities of ListView which includes Insert, Edit and Delete with both LinqDataSource and code. We will follow the previous post example for the data editing.
The first thing you have to do to add the data editing capabilities is defining the Edit item and Insert item templates. Instead of row kind of style editing like the DataGrid and GridView we will follow the form style editing, this is the another powerful features of ListView that you have the full control on how the html is generated. The following shows the screen-shots of both EditItemTemplate and InsertItemTemplate view:


The following shows the EditItem and InsertItem Template:
<EditItemTemplate>
<tr>
<td colspan="9">
<table>
<tbody>
<tr>
<th>
Company:
</th>
<td>
<asp:TextBox ID="CompanyNameTextBox" runat="server" Text='<% #Bind("CompanyName")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="CompanyValidator" runat="server" ControlToValidate="CompanyNameTextBox" ErrorMessage="<div>Company cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Contact:
</th>
<td>
<asp:TextBox ID="ContactNameTextBox" runat="server" Text='<% #Bind("ContactName")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="ContactValidator" runat="server" ControlToValidate="ContactNameTextBox" ErrorMessage="<div>Contact cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Title:
</th>
<td>
<asp:TextBox ID="ContactTitleTextBox" runat="server" Text='<% #Bind("ContactTitle")%>'></asp:TextBox>
</td>
</tr>
<tr>
<th>
Address:
</th>
<td>
<asp:TextBox ID="AddressTextBox" runat="server" TextMode="MultiLine" Columns="20" Rows="4" Text='<% #Bind("Address")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="AddressValidator" runat="server" ControlToValidate="AddressTextBox" ErrorMessage="<div>Address cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
City:
</th>
<td>
<asp:TextBox ID="CityTextBox" runat="server" Text='<% #Bind("City")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="CityValidator" runat="server" ControlToValidate="CityTextBox" ErrorMessage="<div>City cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Postal Code:
</th>
<td>
<asp:TextBox ID="PostalCodeTextBox" runat="server" Text='<% #Bind("PostalCode")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="PostalCodeValidator" runat="server" ControlToValidate="PostalCodeTextBox" ErrorMessage="<div>Postal code cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Country:
</th>
<td>
<asp:TextBox ID="CountryTextBox" runat="server" Text='<% #Bind("Country")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="CountryValidator" runat="server" ControlToValidate="CountryTextBox" ErrorMessage="<div>Country cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Phone:
</th>
<td>
<asp:TextBox ID="PhoneTextBox" runat="server" Text='<% #Bind("Phone")%>' ValidationGroup="edit"></asp:TextBox>
<asp:RequiredFieldValidator ID="PhoneValidator" runat="server" ControlToValidate="PhoneTextBox" ErrorMessage="<div>Phone cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th></th>
<td>
<asp:LinkButton ID="UpdateButton" CommandName="Save" runat="server" Text="Update" ValidationGroup="edit"></asp:LinkButton>
<asp:LinkButton ID="CancelButton" CommandName="Cancel" runat="server" Text="Cancel" CausesValidation="false"></asp:LinkButton>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</EditItemTemplate>
<InsertItemTemplate>
<tr>
<td colspan="9">
<table>
<tbody>
<tr>
<th>
ID:
</th>
<td>
<asp:TextBox ID="CustomerIDTextBox" runat="server" Text='<% #Bind("CustomerID")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="IDValidator" runat="server" ControlToValidate="CustomerIDTextBox" ErrorMessage="<div>ID cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Company:
</th>
<td>
<asp:TextBox ID="CompanyNameTextBox" runat="server" Text='<% #Bind("CompanyName")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="CompanyValidator" runat="server" ControlToValidate="CompanyNameTextBox" ErrorMessage="<div>Company cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Contact:
</th>
<td>
<asp:TextBox ID="ContactNameTextBox" runat="server" Text='<% #Bind("ContactName")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="ContactValidator" runat="server" ControlToValidate="ContactNameTextBox" ErrorMessage="<div>Contact cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Title:
</th>
<td>
<asp:TextBox ID="ContactTitleTextBox" runat="server" Text='<% #Bind("ContactTitle")%>'></asp:TextBox>
</td>
</tr>
<tr>
<th>
Address:
</th>
<td>
<asp:TextBox ID="AddressTextBox" runat="server" Text='<% #Bind("Address")%>' TextMode="MultiLine" Columns="20" Rows="4" ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="AddressValidator" runat="server" ControlToValidate="AddressTextBox" ErrorMessage="<div>Address cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
City:
</th>
<td>
<asp:TextBox ID="CityTextBox" runat="server" Text='<% #Bind("City")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="CityValidator" runat="server" ControlToValidate="CityTextBox" ErrorMessage="<div>City cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Postal Code:
</th>
<td>
<asp:TextBox ID="PostalCodeTextBox" runat="server" Text='<% #Bind("PostalCode")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="PostalCodeValidator" runat="server" ControlToValidate="PostalCodeTextBox" ErrorMessage="<div>Postal code cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Country:
</th>
<td>
<asp:TextBox ID="CountryTextBox" runat="server" Text='<% #Bind("Country")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="CountryValidator" runat="server" ControlToValidate="CountryTextBox" ErrorMessage="<div>Country cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th>
Phone:
</th>
<td>
<asp:TextBox ID="PhoneTextBox" runat="server" Text='<% #Bind("Phone")%>' ValidationGroup="add"></asp:TextBox>
<asp:RequiredFieldValidator ID="PhoneValidator" runat="server" ControlToValidate="PhoneTextBox" ErrorMessage="<div>Phone cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<th></th>
<td>
<asp:LinkButton ID="InsertButton" CommandName="Insert" runat="server" Text="Save" ValidationGroup="add"></asp:LinkButton>
<asp:LinkButton ID="CancelButton" CommandName="Cancel" runat="server" Text="Cancel" CausesValidation="false"></asp:LinkButton>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</InsertItemTemplate>
To edit existing record, you have to put a Button in the ItemTemplate/AlternatingItemTemplate which commandName is set to Edit, you can also use ImageButton/LinkButton instead of regular button as in this example I am using LinkButton. The same holds true for deleting record, in this case the commandName has to be Delete. Next, in the EditItemTemplate you have to add buttons which commandName sets to Update and Cancel.
To add record, you must have to set the InsertItemTemplate position, the supported values are FirstItem, LastItem and None. Since we are using form view for adding record it does not look appealing showing it all the time, instead we will put a button in the LayoutTemplate, upon click the form view will appear at the end. Next, in the InsertItemTemplate two new buttons are required which commandName sets to Insert and Cancel. At last, as we are showing this template on the new button click, we need to put few lines of code to show/hide the template as well as the new button. The following shows the code:
private void CloseInsert()
{
lvwCustomers.InsertItemPosition = InsertItemPosition.None;
((LinkButton) lvwCustomers.FindControl("NewButton")).Visible = true;
}
protected void NewButton_Click(object sender, EventArgs e)
{
lvwCustomers.EditIndex = -1;
lvwCustomers.InsertItemPosition = InsertItemPosition.LastItem;
((LinkButton) sender).Visible = false;
}
protected void lvwCustomers_ItemEditing(object sender, ListViewEditEventArgs e)
{
CloseInsert();
}
protected void lvwCustomers_ItemCanceling(object sender, ListViewCancelEventArgs e)
{
if (e.CancelMode == ListViewCancelMode.CancelingInsert)
{
CloseInsert();
}
}
protected void lvwCustomers_ItemInserted(object sender, ListViewInsertedEventArgs e)
{
CloseInsert();
}
That's it we are all set to Add/Edit/Delete the records with LinqDataSource control.
Now we will see how to do the same with code. It is almost same as the LinqDataSource except few things, we need to hook the ItemCommand event and add few empty event handlers, the following shows the code:
protected void lvwCustomers_ItemCommand(object sender, ListViewCommandEventArgs e)
{
switch(e.CommandName)
{
case "Insert":
{
InsertCustomer(e.Item);
break;
}
case "Update":
{
UpdateCustomer(e.CommandArgument as string, e.Item);
break;
}
case "Delete":
{
DeleteCustomer(e.CommandArgument as string);
break;
}
}
}
protected void lvwCustomers_ItemInserting(object sender, ListViewInsertEventArgs e)
{
}
protected void lvwCustomers_ItemEditing(object sender, ListViewEditEventArgs e)
{
CloseInsert();
lvwCustomers.EditIndex = e.NewEditIndex;
BindList();
}
protected void lvwCustomers_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
}
protected void lvwCustomers_ItemCanceling(object sender, ListViewCancelEventArgs e)
{
if (e.CancelMode == ListViewCancelMode.CancelingInsert)
{
CloseInsert();
}
else
{
lvwCustomers.EditIndex = -1;
}
BindList();
}
protected void lvwCustomers_ItemDeleting(object sender, ListViewDeleteEventArgs e)
{
}
As you can see that in ItemCommand event we are checking the commandName and based upon that we are calling the helper function which does the actual db operation. The reason behind having those empty event handlers like ItemInserting, ItemUpdating and ItemDeleting is, if those left unhandled you will get a nasty exception that the event was not handled. While playing with the ListView data editing my first try was these events to do the db operation instead of the ItemCommand, but found that the event argument always left empty(except the itemIndex) as well as the EditItem and InsertItem property of ListView if the ListView is not bind with any DataSource control.
Now lets see what those db operation functions does, the following shows the code:
private void InsertCustomer(ListViewItem insertItem)
{
if (IsValid)
{
using (NorthwindDataContext db = new NorthwindDataContext())
{
Customer customer = new Customer();
customer.CustomerID = ((TextBox)insertItem.FindControl("CustomerIDTextBox")).Text;
customer.CompanyName = ((TextBox)insertItem.FindControl("CompanyNameTextBox")).Text;
customer.ContactName = ((TextBox)insertItem.FindControl("ContactNameTextBox")).Text;
customer.ContactTitle = ((TextBox)insertItem.FindControl("ContactTitleTextBox")).Text;
customer.Address = ((TextBox)insertItem.FindControl("AddressTextBox")).Text;
customer.City = ((TextBox)insertItem.FindControl("CityTextBox")).Text;
customer.PostalCode = ((TextBox)insertItem.FindControl("PostalCodeTextBox")).Text;
customer.Country = ((TextBox)insertItem.FindControl("CountryTextBox")).Text;
customer.Phone = ((TextBox)insertItem.FindControl("PhoneTextBox")).Text;
db.Customers.Add(customer);
db.SubmitChanges();
CloseInsert();
BindList();
}
}
}
private void UpdateCustomer(string customerID, ListViewItem editItem)
{
if (IsValid)
{
using (NorthwindDataContext db = new NorthwindDataContext())
{
Customer customer = db.Customers.Where(c => c.CustomerID == customerID).Single();
customer.CompanyName = ((TextBox) editItem.FindControl("CompanyNameTextBox")).Text;
customer.ContactName = ((TextBox)editItem.FindControl("ContactNameTextBox")).Text;
customer.ContactTitle = ((TextBox)editItem.FindControl("ContactTitleTextBox")).Text;
customer.Address = ((TextBox)editItem.FindControl("AddressTextBox")).Text;
customer.City = ((TextBox)editItem.FindControl("CityTextBox")).Text;
customer.PostalCode = ((TextBox)editItem.FindControl("PostalCodeTextBox")).Text;
customer.Country = ((TextBox)editItem.FindControl("CountryTextBox")).Text;
customer.Phone = ((TextBox)editItem.FindControl("PhoneTextBox")).Text;
db.SubmitChanges();
lvwCustomers.EditIndex = -1;
BindList();
}
}
}
private void DeleteCustomer(string customerID)
{
using(NorthwindDataContext db = new NorthwindDataContext())
{
Customer customer = db.Customers.Where(c => c.CustomerID == customerID).Single();
db.Customers.Remove(customer);
db.SubmitChanges();
}
Pager dpCustomers = (Pager)lvwCustomers.FindControl("dpCustomers");
dpCustomers.CurrentPageNo = 1;
BindList();
}
That's it. You will find the complete source code of both of this example in the bottom of this post. In my next post, I will show the new GroupTemplate of ListView.
Download: Full Source
