Sunday, December 14, 2008

Simplify Inline Editing using GridView, GridViewControlEmbedder ASP.NET AJAX Extender.

I created another nice ASP.NET AJAX extender for GridView.

With it's help you can enable inline editing for Grids in web pages relatively faster, comparing to time needed to implement this feature from scratch using pure JavaScript.

For example having the following GridView on the page:

image

You can add an extender that allows editing of values of cells right in place:

image

Extender has inner elements named "GridViewControlEmbedderColumn", each of this elements enabled inline editing for one column in the GridView. ColumnIndex attribute points to client side cell index - for example 0 will point to "First Name" column. ControlType can have one of several values. For now extender supports two types of embedded controls for cells - "TextBox" and "ComboBox". CssClass attribute is optional and if you set it to some css class it will be assigned to embedded control when it's created.

Extender itself has several important properties and events.

HoverCssClass - set here the css class that will be assigned to cell's in GridView when they are hovered (hover css class will work only for cells having embedded control assigned to them).

TargetControlID - set here ID of target GridView control, this extender should extend.

RowIdAttributeName - set here name that will be used as name for client side attribute when GridView rows are rendered. It is important to understand that values that will be entered into each cells are identified using column index and row id - some data-driven value that will identify row uniquely. The ID itself can be set in event handler for OnGridViewControlEmbedderRowReady event (see below).

OnGridViewControlEmbedderRowReady - subscribe to this event to set row IDs initial cell values for each row and cells in it before GridView renders. Client side script has to know this values to correctly populate/accumulate values that will be later available on server side after postback.

For the sample markup showed above this event handler will look like this:

image 

Lets review this code in more details.

First line of code just gets to the DataItem property of the GridView Row, in the sample I use XmlDataSource and this code allows to get to the original XPathNavigator to be able to retrieve values from the datasource for that row. It's important to mention that this event is raised for each row of GridView in synch with OnRowDataBound event raised originally by GridView. So in this event handler you can do the same manipulation over row/cells as you could in OnRowDataBound event handler.

Now there are two properties of EventArgs object that need to be set. These are e.RowID and e.CellValues.

Set some unique value to e.RowID (type of string) - this will be used later to retrieve edited values in the grid.

Also set initial values for cells to e.CellValues collection (dictionary<int, string>) - they will be used on the client side initially on first inline editing operation for each cell.

 

The result of using three small code blocks showed above is show on this picture:

image

Vertical scroll appeared for our GridView thanks to another extender I created earlier (Cross Browser GridView Fixed Header Extender, ASP.NET AJAX)

As you see You can simply click any cell and edit it's value - extender allows to do it without going to the server - all this functionality is done on the client.

Now when you do postback (for example user clicks "Save" button), you have access to all values that user have edited in "Values" property of extender.

This property is of type "Dictionary<string, Dictionary<string, string>>" - here outer dictionary's keys are row ids and inner dictionary's keys are cell indexes.

For example using this code we can handle / validate / and process all cell values edited by the user:

image

so for example let's edit two rows in the grid an click "Submit":

image

 

Extender has nice API on the client side as well. It allows to control the process of cells editing, and allows to simplify really complex scenarios.

Please review GridViewControlEmbedder.js file and especially functions inside "Public methods" region.

Extender has two client side events, to which you can subscribe to control/validate editing on the client side:

beforeCellEdit - occurs before cell is entered for edit.

afterCellEdit - occurs before cell is left and it's edited value is committed to the client-side state.

You can subscribe to these events in javascript using simple syntax:

image

where c.ce in string - ClientID of the extender.

Extender passes custom event args passed to the event handlers .

Constructors look like this:

image

 

As you can see you can cancel entering or exiting the cell by setting cancel property of event args. You can analyze the information about currently edited cell, row, column index, inner text of cell, also you have a reference to the grid from there. There is also "callBack" property that can be used to perform some asynchronous operation before continuing cell edit. For example you can set cancel property of event agrs to true to prevent continuing of current operation (entering/exiting cell), issue request to the server, and only after getting response you can call e.get_callBack() property - which is pointer to the function in the extender that will complete the operation.

I also created second demo page that shows how to use client side events of the extender to use inline editing with ComboBoxes. Here is the screenshot:

image

Demo page demonstrates using of drop down lists for choosing options for each cells. what options will be shown for what cell or row is pretty controllable from javascript code. Here is the code snippet from this demo page:

image

Here I call web service for options, passing it column index, then I set cancel property of event args to true to prevent entering the cell. After response is get back from the server, I grab the reference to the select element (e.get_control()) and fill it with options containing information got from the server. Finally I call callBack provided again in event args to enter cell and allow user to edit it.

 

Finally here is a full source code for the control and demo pages. Download and use it,

Hope this helps.

 

P.S.

I am thinking about moving all my control's source code - I write about in this blog, to the Codeplex project. This version of controls library also contains some fixed and upgraded controls from earlier blog posts - HoverTooltip, GridViewFixedHeaderExtender. I think I will move the source to the Codeplex about next week. Also I will update all old blog posts to link to Codeplex project for controls suite download.

kick it on DotNetKicks.com

5 comments:

Anonymous said...

Hi,

Nice article. I wondering if attaching callbacks to each row is expensive or now. I would image a grid with lot of rows and columns would respond very slowly.

What if the event handlers are attached to the table instead and get the target cell on the fly.

Thanks,
Alomgir Miah

Kirill Chilingarashvili said...

Hi Alomgir,

I don't think it will be very much faster (in case of attaching handlers to grid instead of each row), at least I used it with about thousand of rows and it worked pretty well.

If not use AJAX and save-at-one-go approach as I supposed in the article - sending 1000 row grid back and forth will be considerably slower.

I think the best way is to use paging in the grid, and also use AJAX to load pages, then having page equal to approximately 100-200 rows will work fine and user will have to deal with less information at one load.

Regarding attaching handlers to main table only - this is possible.
However how about finding the original cell that raised hover, mouseout, click, mousedown and another event.. Each cell can have another tables inside it, with another cells and rows, and finding the correct one on each hover event may be slower. Overall I think it will be not much faster (if faster at all).

Regards,
Kirill

Achutha Krishnan said...

Hello,

Your Grid Extender seems to be really really good and fast as well. It was a good idea that only the updated rows are fired on "Submit" click. This will be really useful for many people. Hey, isn't it possible to make the whole row go into edit mode when it is clicked on it? Because, when there are more than 20 columns, it would be really difficult to click each and every cell and modify it.

kosmasjack said...

Thanks for your excellent article. Would you mind let me know how to fixed the footer row and some columns?

Php Development India said...

Really thankful post shared here by you and also every time you post something different thing.