Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Managing lists of items from databases is the bread and butter of most web applications, but creating a user-friendly grid can be problematic. The obvious solution of creating Microsoft grid controls in an AJAX.NET UpdatePanel can create very large HTML pages with long response times when a substantial amount of data is being worked with. Also, every grid has to be created individually on each page, and that makes it hard to create and maintain a common "look and feel".

This lightweight grid outputs very compact HTML, and uses JavaScript to dynamically generate input or select boxes when updating. It includes built-in AJAX-ed sorting and filtering which is very user-friendly, and throws in CSV export for free. It has a fixed table header and scrolling contents, to facilitate use with large amounts of data.

Many different grids can be very quickly generated in a standard way, using an XML control file and a single common style sheet. Multiple AjaxGrids can be defined and used, even on the same page.

You can customise grid column data to include links, images, etc., and to support your own custom validations.

Sample picture

Background

Most developers will dip their toes into the seductive water of AJAX.NET by using the UpdatePanel, but calling AJAX methods directly can be much more flexible and efficient.

Any HTML element that is updated server-side by ASP.NET code must contain a long and "decorated" ID, and these are used extensively in a typical grid and UpdatePanel. But, if you generate your HTML directly, then you can rely on JavaScript to walk through the DOM to assign events to the element, to pop up HTML elements dynamically for the user to edit into a request, and to identify any element that has changed. AJAX allows you to do most of the heavy lifting server-side, so that you don't find yourself attempting to write an entire application in JavaScript (a real performance "gotcha").

Because the HTML is so much more compact, there is much less data to transfer. Large numbers of rows can be returned efficiently to users. This implementation includes a way of side-stepping paging in most cases and under user control, even for large amounts of data. With the fixed grid header and scrolling data section, the user's scrolling experience can really take advantage of the smooth scrolling and background updating, which browsers are optimised for. This approach is especially kind to your dial-up browsers.

This control remembers the user's preferences for each grid during the session, and can be set up to remember them between sessions too. Thanks to AJAX.NET, the user's data in the browser can also be always available server-side; you could extend the control to remember all the data that the user enters (even on a keystroke-by-keystroke basis), and even allow switching off the browser mid-session and resume working later at the same point - just as if it was a local application. Naive users especially will appreciate the security of not losing entered data by mistake.

Using the Code

The source files are set up as a complete ASP.NET 2.0 website.

This code uses two tables of a database set up by the open source "Tackle" application for management of the SCRUM process, which itself uses AJAX.NET using an UpdatePanel. It is an interesting comparison. You can download it from here. (I have also included a minimal database .mdf file for a sample database, if you don't want to bother with this.)

You will need to edit the connection string in AjaxGrid.db.cs. It assumes a SQLExpress implementation of the database.

Your own implementation of this control will almost certainly include an edited stylesheet to match your own application branding (you might like to improve the graphics too!).

Lastly, you will want to edit ajaxgrid.xml to define and format each grid in your web application.

The Source Files

This control doesn't work well as a completely stand-alone assembly, because you need to include its .css and .js files and ajaxgrid.xml in the containing website. You will probably also want to adapt the core source files to the needs of your web application.

What the Code Does

The server "manually" generates a string containing HTML using a StringBuilder object. The HTML can then be used to populate the control server-side in C#:

divGrid.InnerHtml = AjaxGrid.Paint(Session["GridProfile"], gridId);

or to populate the control client-side from an AJAX callback in JavaScript:

divGrid.innerHTML =  response.html;

HTML Layout

When the "add new row" image is clicked, an extra row is added to the header table. This allows the user to see and edit the new row wherever the data table is scrolled to; once the columns have been filled in and the new row is accepted, the tables will automatically be repainted and the new row will then appear in the data table in its proper sort order.

Points of Interest

This control illustrates an interesting technique for making texts set up by JavaScript language-independently, by calling back to the server via AJAX. In many cases, the same thing can be accomplished by customising the JavaScript file to each individual user, but calling back to the server on demand reduces the amount of unnecessary data sent to the browser and can be easier to maintain.

An AJAX application will present differently on your local machine (where there is little latency) and on the Internet. Although the application correctly handles multiple outstanding AJAX calls, it can be helpful to the user to show an animated GIF file while an AJAX response is outstanding. There are no definite rules about whether to initiate an AJAX call on every key-press or when the "onchange" event handler fires (except beware if some other JavaScript event click handler is called by (say) clicking a button, you may lose notification of the onchange operation). Where a new row is to be created, all columns must be default or filled in before creating a new row in the database, and it is necessary to have a button to indicate that the row is now ready to be created. Nevertheless, it is convenient to AJAX each column up to the server as it is completed.

One possible implementation of updates from the grid is to save them up in the user's Session data and only actually action the updates when the user explicitly requests with a "Save" command. This mimics many desktop applications in allowing the user to see how the updates look before committing them to the database. There are implications to this if multiple users can see the same data, especially if the saved updates are stored between Sessions. But even without these refinements, there is always going to be a window between the user reading and writing row data.

Using database locks (possibly supplemented by SQL Server notifications when row data changes) are complex to implement, and may significantly impact performance. A lightweight solution is to record when and who the data was last changed by, and notify the user if that differs from when the row data was read. In many cases, users can be quite tolerant of being told that they must repeat the edit and update, because in the real world, it is often an error if two people mistakenly work on the same task at the same time. In the following SQL snippet from a stored procedure to update a row, contention is detected before the update is action-ed, and the update is abandoned if someone else has edited the row since the original data was read.

    -- @editdate DATETIME parameter is the date that
    --  the record was last edited (remembered from read)
    -- @userid INT parameter is the caller's id
    -- @newdate DATETIME OUT parameter is the new date
    --  that the record was last edited (or null if not updated)
    -- @editid INT OUT parameter is the person who made that last edit

    -- each table so protected needs two new columns:
    -- editdate DATETIME is the date last edited
    -- updaterId INT is the person who last updated the record

    IF @editdate IS NOT NULL
    BEGIN
        SELECT @editid = updaterId FROM [table] 
            WHERE rowid=@rowid AND editdate <= @editdate
        IF @editid IS NOT NULL AND @editid <> @userid
        BEGIN
            SELECT @newdate =NULL
            RETURN
        END
    END
    SELECT @newdate = GETDATE()
    SELECT @editid = @userid

By the way, if the last editor was the caller, then that is considered okay. It is possible that the user has created problems for themselves by working from two different machines or two different browsers, but most likely, they will be quite annoyed if the website prevents them from updating their own edits.

You could also change the AJAX callback when each cell is updated to include both the new and the original values (the original value is still present despite being overlaid by the edit popup). This allows a more fine-grained approach where the update is refused only if a particular column of the row has been changed by someone else, rather than the entire record.

One major source of difficulty in a web application is the management of postbacks and the random use of the Back button. This is much less of a problem for AJAX-enabled applications, because the user cannot initiate the same operation twice by refreshing while waiting a response, and the Back button will take the user right out of the current page. If you have a button which mustn't be pushed twice, add some custom JavaScript to disable it when pressed.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGreat control but could be better
MariuszGola
8:22 2 Jul '08  
Why not combine some of these js css files and make a server control instead of a custom control. Just a suggestion.
GeneralRe: Great control but could be better
Simon Gulliver
6:23 4 Jul '08  
I must say I have never paid much attention to server controls - are they not just a lot of extra interface to allow an assembly with the same functionality as a user control to be dropped into the page via the toolbox ? Since this is open source, you need the source files anyway don't you ?

So I think there's not much difference between user and server controls, in terms of their underlying functionality.

Your idea - as I understand it - of putting the .css and .js directly into the page (rather than held as separate files) is an interesting one; it would certainly work when building the grid server-side but I'm not sure whether the css and javascript could be amended client-side after an AJAX call. Could you have some javascript which acted as a javascript loader ? If so, you could do all kinds of clever things - although a server-side javascript generator and emulator might be a useful tool if you were attempting this. Hmmm...
GeneralWrong approach for scrolling part
nsimeonov
23:56 1 Jul '08  
This way you're crippling browser's HTML rendering capabilities and fixing column widths. When you put controls in the grid (like DropDownLists) you will have to set fixed size for it too or it will break the layout. Not only that but setting grid to 100% etc will produce really "interesting" results.

I recommend you to check the following example: http://www.imaputz.com/cssStuff/bigFourVersion.html[^]

It's pure CSS and eventhough it contains more styles than neccessary I was able to cut it down to only what I need. Also with a bit of javascript you can do even better. Yes it requires more efforts but we're talking about a control so it's one time effort only.
GeneralRe: Wrong approach for scrolling part
Simon Gulliver
2:51 2 Jul '08  
Your example doesn't work with IE7, and goes out of sync in Firefox 3 when there is a long string of data without a whitespace (for example, a long URL) or a dropdown with long option text. That said, your example shows an alternate method of data layout which differs in behaviour and may be more appropriate for your particular data and application - the framework can relatively easily be changed to accomodate this.
GeneralAjaxDataControls
Sonu Kapoor
9:15 29 Jun '08  
Have you considered to take a look at the AjaxDataControls?

http://codeplex.com/ajaxdatacontrols
http://dotnetslackers.com/projects/ajaxdatacontrols


GeneralRe: AjaxDataControls
Simon Gulliver
3:06 2 Jul '08  
I may be revealing my age a bit, but I can see parallels to the debate on making ActiveX controls with MFC vs ATL. MFC is a large, well-integrated framework with a host of features but ATL is an alternate "lean and mean" approach which many people (including me) preferred. This article is all about showing an alternate approach to creating a grid control which drives SQL efficiently and shifts the minimum amount of data down the wire.
GeneralRe: AjaxDataControls
Sonu Kapoor
4:22 2 Jul '08  
I understand, however why would you re-invent the wheel? The AjaxDataControls comes with the full source, so that you can see how it was created. That was the purpose to make it open-source.


GeneralRe: AjaxDataControls
praveen.battula
23:23 24 Nov '09  
Hi,
I like your project and which is very simple and well customized.
But, according to my requirement, the grid would looks like editable means all columns [Which are not readonly] should have controls loaded instead of label. In the current one, when we click the cell then only the realted control is loading. But, by default, all controls should be loaded and when I click Save button then only all records will be saved to database. Right now, when we complete changes on the cell it is immediately saving them to database.

Can you please quickly tell me is this possible or not?
Once again I am just mentioning the points I needed.
1. All edtable columns should load with the controls instead of tezt in all controls.
2. Data should be saved to database when user clicks on SAVE. This SAVE button should be at bottom.

Otherwise, tell me the ways we can do, I will try to develop it. Please help me out. Any ideas are valuable to me.
GeneralRe: AjaxDataControls
Simon Gulliver
10:09 7 Dec '09  
A good design pattern I have used is to save edited information in an object held in the session. When an object is marked "dirty" then a Save button is made visible, and when Save is pressed the record is written to the database. In the project I presented I didn't go to this extra level of complexity, especially because sorting and filtering is much easier using SQL than implementing with objects.

I would recommend that you keep an array of dirty objects in the session so that you can edit several columns in the row before pressing save. You can even save them over sessions if you use SQL to save the sessions or if you capture the end-seesion event in global.aspx.

You will need a bit of javascript to make a Save button visible when any row is changed and a standard naming convention for the save button.

I really recommend that you popup the edit boxes one at a time while you are editing a row because either you will have issues with Viewstate or it gets complex managing multiple editable columns.

Hope this helps


Last Updated 28 Jun 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010