![]() |
Web Development »
Ajax and Atlas »
General
Intermediate
License: The Code Project Open License (CPOL)
Light Speed Inline Editing Using ASP.NET AJAX and Web Services: Part IBy Andrew GolikJavaScript + AJAX solution for inline editing in a grid. |
C#, Javascript, XML, HTML, Windows, .NET2.0, ASP.NET, Visual-Studio, Ajax, Dev, Design
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

If you ask people what the most important thing that they want for web based application UIs, most of them will answer "I want to edit data really fast, like in Excel!" That is a tough request. It means that the person wants to click on a row, edit some data, then click on another row, and have all the data saved into a database. We've found a solution for this during TargetProcess v.2.6 development. TargetProcess is a great agile project management software (if you are not aware ;)
The ASP.NET framework provides inline editing functionality in GridView. But sometimes, you get into situations when you cannot use this functionality:
UpdatePanel, AJAX.NET still will update the whole grid instead of just one row, and the size of the date in the post back remains almost the same (and large). With bad performance, it will not be like Excel, right? So, we have to look for a solution that:
The Inline Edit Controller (IEC) is JavaScript solution that resolves all our problems (you will see it really soon). It has the following advantages:
IEC is a plain JavaScript solution that allows editing rows in a grid. In order to have an editable row, you should mark the row in some specific manner. Let's call this stuff "Inline Edit Panel". It contains a button that enables inline editing as well as buttons that save changes and cancels editing. In fact, the better way to go is to enable editing on double click, and cancel it on Escape key. That will be shown in more advanced examples. So far, we have simple buttons:
<asp:GridView CellPadding="0" CellSpacing="0" Width="1px"
GridLines="none" runat="server" AllowSorting="false"
AutoGenerateColumns="false" ID="uxOrders">
<asp:TemplateField>
<ItemTemplate>
<span runat="server" style="white-space: nowrap"
class="inlineEditIcon" inlineeditattribute="true"
rowid='<%# Eval("OrderID")%>'>
<img runat="server" action="edit"
title="Inline Edit" src="~/img/edit.gif" />
<img runat="server" style="display: none"
action="save" title="Save" src="~/img/save.gif" />
<img runat="server" style="display: none"
action="cancel" title="Cancel" src="~/img/cancel.gif" />
</span>
</ItemTemplate>
</asp:TemplateField>
...
Cells that are going to be editable must be located in a specific place holder as well. Let's call this stuff "Edit Area".
<asp:TemplateField HeaderText="Freight">
<ItemTemplate>
<span id="FreightIdPrefix<%# Eval("OrderID")%>">
<asp:Label ID="Label1" runat="server"
Text='<%#Eval("Freight")%>'></asp:Label>
</span>
</ItemTemplate>
</asp:TemplateField>
As you can see, the "Inline Edit Panel" has three buttons, with an extra "action" attribute. Each action attribute fires the corresponding event. The IEC handles all these events and makes the corresponding changes in the DOM. Let's review the editing process now.
id="FreightIdPrefix777". The idea looks really simple. But maybe, the solution is hard to use, who knows… Only a real example can help us judge the solution.
Let's take an ASP.NET example with:
GridView control to present the data. Order class. We want to show all the orders in a grid.

GridView initialization is a no-brainer as well. OrderService is a class that can retrieve orders from the database (we don't care how; maybe the GetAllOrders method uses NHibernate, or maybe plain old SQL).
protected override void OnLoad(EventArgs e)
{
if (!IsPostBack)
{
OrderService orderService = new OrderService();
Order[] orders = orderService.GetAllOrders();
uxOrders.DataSource = orders;
uxOrders.DataBind();
...
<asp:TemplateField>
<ItemTemplate>
<span id="Span1" runat="server" style="white-space:
nowrap" class="inlineEditIcon" inlineeditattribute="true"
rowid='<%# Eval("OrderID")%>'>
<img id="Img1" runat="server" action="edit"
title="Inline Edit" src="~/img/edit.gif" />
<img id="Img2" runat="server" style="display: none"
action="save" title="Save" src="~/img/save.gif" />
<img id="Img3" runat="server" style="display: none"
action="cancel" title="Cancel"
src="~/img/cancel.gif" />
</span>
</ItemTemplate>
</asp:TemplateField>
And insert all the required "Edit Areas".
<asp:GridView CellPadding="0" CellSpacing="0" Width="1px"
GridLines="none" runat="server" AllowSorting="false"
AutoGenerateColumns="false" ID="uxOrders">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<span priorityname='<%#Eval("Priority")%>'
id="PriorityIdPrefix<%# Eval("OrderID")%>">
<span>
<%#GetPriorityHTML(Container.DataItem)%>
</span
</span>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Freight">
<ItemTemplate>
<span id="FreightIdPrefix<%# Eval("OrderID")%>">
<asp:Label ID="Label1" runat="server"
Text='<%#Eval("Freight")%>'></asp:Label>
</span>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Ship Name">
<ItemTemplate>
<span id="ShipNameIdPrefix<%# Eval("OrderID")%>">
<asp:Label ID="Label1" runat="server"
Text='<%#Eval("ShipName")%>'></asp:Label>
</span>
</ItemTemplate>
</asp:TemplateField>
...
As you can see, the "Edit Areas" have a composite ID that consists of two parts: "Edit Area prefix + itemID". Such an ID is required to make each row unique.
EditAreaSettings instances (see below) for each "Edit Area".
var editAreasSettings = new Array();
//ShipName is a property of Order class
//ShipNameIdPrefix is an id of span element
//(EditArea of ShipName property)
var shipNameEditAreaSettings = new EditAreaSettings("ShipName",
"ShipNameIdPrefix",null, true);
editAreasSettings.push(shipNameEditAreaSettings);
var freightSettings = new EditAreaSettings("Freight","FreightIdPrefix");
freightSettings.onSelectValue = onSelectFreightValueHandler;
editAreasSettings.push(freightSettings);
var prioritySettings = new EditAreaSettings("Priority",
"PriorityIdPrefix","uxPriorities");
prioritySettings.onRenderEditedValue = onRenderPriorityValue;
prioritySettings.onSelectValue = onSelectPriorityValue;
editAreasSettings.push(prioritySettings);
var inlineEditController = new InlineEditController('<%=uxOrders.ClientID%>',
editAreasSettings, onSaveEventHandler, true);
Arguments:
areaName – used by IEC to map the edited values to the retObj that is passed into onSaveEventHandler. areaPrefix – used by IEC to find the edit area. dataSourceControlID – data source control ID that has a collection of predefined values. onRenderPriorityValue and onSelectPriorityValue are custom handlers that are implemented outside of IEC. It is impossible to implement all the cases of editing, and for specific situations, you have to create custom handlers. For example, the Priority column is an image. When the row is in the usual state, it shows the priority icon, but when the row is in an editable state, it should show the select box.

The code of onRenderPriorityValue and onSelectPriorityValue are out of the scope for this article; it will be described in Part II (architecture, extensibility, and stuff like that).
function onSaveEventHandler(retOb)
{
retObj.OrderID = retObj.itemID;
TargetProcess.DaoTraining.BusinessLogicLayer.OrderService.UpdateOrder(retObj,
onRequestComplete,
onErrorRequest);
}
The UpdateOrder method will be fired on the save event. It has only one input parameter, retObj, which holds all the edited values. IEC maps the edited values into retObj using the keys from EditAreaSettings (for example, shipName, Freight). Fortunately, I mapped the "Edit Areas" for IEC to be consistent with the Order class, except retObj.itemID that is created by IEC. As a result, retObj can be casted to the Order class. I had to do one extra assignment to make retObj completely consistent with Order.
retObj.OrderID = retObj.ItemID
The onSaveEventHandler handler is completely consistent with the web method.
[ScriptService]
public class OrderService : WebService
{
[WebMethod]
public string UpdateOrder(Order order)
{
OrderDao OrderDao = DaoFactory.GetDaoFactory().GetOrderDao();
bool isUpdated = OrderDao.UpdateOrder(order);
if (isUpdated)
return "The order '" + order.OrderID + "' is successfully updated.";
else
return "Unable to find order '" + order.OrderID + "'.";
}
…
I'm using the web service to save the order, but it's not necessary. It's a pretty flexible approach that allows other mechanisms to save the data.
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 6 Apr 2008 Editor: Smitha Vijayan |
Copyright 2007 by Andrew Golik Everything else Copyright © CodeProject, 1999-2010 Web21 | Advertise on the Code Project |