Published: 28 Nov 2007
By: Ralf Saha

Learn how to implement the NetAdvantage UltraWebGrid in a professional application environment.

Introduction

The UltraWebGrid control provided by Infragistics offers abundant functionality. However, implementing the UltraWegGrid in a professional application environment requires some thinking and customization as well. This article describes a real life implementation scenario.

You should not expect that the UltraWebGrid does the job right out of the box. Although the code base of the web controls from Infragistics is well documented, those who are new to the UltraWebGrid might want to see a complete picture of how it can be implemented under real life conditions. Currently no place in the web seems to be providing this for free.

In this article I address a couple of important findings that should be relevant for any developer using the UltraWebGrid. Some aspects go beyond the Infragistics stuff and should be considered general design recommendations. I expect that you are already familiar with the basic functionality of the UltraWebGrid control and will not explain basics such as how to add and configure grid columns. It is also recommended that you make yourself familiar with custom inheritance controls and user controls.

The source code of the application I am using is monstrous, and cannot be shared in this article. Please do not expect source code that you can just copy and use. I am just using code snippets for better understanding. However, the code is presented in a meaningful context covering code behind and client script. You need to transfer this knowledge into your context. It took me a couple of days to find out about all these issues and I hope this article can prevent you from going through the same painful experience.

In this article the Client Side Object Model of the Infragistics controls is referred to as CSOM.

I will refer to the UltraWebGrid as CustUltraWebGrid because I use a custom control that inherits from UltraWebGrid. If you don't use a custom control, the grid is referred to as UltraWebGrid.

The Task

  • Implement the grid with all data editing related functionality enabled. The objective is to create a friendly user interface keeping in mind that this will result in more load for the server.
  • The application must support remote users; therefore page state must not be transmitted in the view state included in the page. Page state persisting should be used instead. The page state persistence medium shall be configurable in session, cache, database or even the file system.
Note: Do not try to code around the page state of the UltraWebGrid. You need the page state in order to enjoy the best features of the grid. I tried to get along without page state using the control state of the grid. However, I had to abandon this attempt for the sake of productivity and returned to using page state. Now I persist the page state on the server instead of moving it across the wire as the page's view state.

Desired Behaviors of the Grid Control

The desired behaviors explained below are definitely worth reading. I believe that much of that matches with what you would expect from a well designed grid control.

The records shall be displayed as a hierarchy therefore multiple bands must be supported.

1. New rows must be added on the client side without resubmitting the page after a row was added until the save button for the grid or for the entire page is pressed.

2. Rows shall be deleted by clicking on a delete button in the row. The delete key on the keyboard shall be disabled. A post back shall be initiated whenever a row is deleted which exists in the database already. The code behind on the server will then actually delete the record from the database.

3. New rows that have not been submitted or that did not pass validation shall be deleted on the client side without resubmitting the page after the row was deleted.

4. The default add box of the UltraWegGrid which contains the buttons for adding new rows shall be replaced by an image button. Clicking the image button shall add a new record to the highest level of the grid without causing a post back.

5. Adding new child records to an existing parent row shall be handled by an add button in the grid row as this is by far more user friendly than the default add buttons for multiple bands provided by the grid.

6. The rows in the grid shall provide buttons that redirect from the list view to the editable form view of the selected entity. These buttons shall be functional only for those records that are already present in the dataset. No post back shall be caused when the record has not been submitted or failed validation.

The final product should look like the following example of a multiband grid that is used by the configuration client of a web application:

Figure 1: Screenshot of the final product

fig01.jpg

Don't get confused by the German shown in the grid. The application supports database configured localization. I have not yet translated this screen.

At the top of the page you can see a webpage with an UltraWebToolbar provided by the master page assigned to the page. Then we have an UltraWebTab managing the access to the functions of the module chosen. Below the tab items there is another UltraWebToolbar control. This toolbar is part of the webpage. Finally there is an UltraWebGrid which supports up to 6 bands. Each row has got a green add button and a red delete button. Clicking the add button adds a new child row to the selected row. Clicking the delete button deletes the selected row. All the behaviors explained previously are fully implemented.

The add button for adding a row to the highest level of the hierarchy can be found at the bottom of the grid control. There is a bar comprising of four asp image buttons.

Figure 2: Bar with command buttons

fig02.jpg

The following screenshot shows a grid which includes the buttons for adding and deleting new records as well as two buttons that cause a postback and a server transfer to the detail pages.

Figure 3: The grid with inline command buttons

fig03.jpg

Implement the UltraWebGrid as an Inheritance Control

The grid control itself should be implemented as a custom control. I suggest that you create a custom control that inherits from the UltraWebGrid class. All standard functionality that you need in connection with a grid should be implemented in that custom control. My implementation has got almost everything, but that is by far too much to share at this point. The following example shows only the code regions for the functionality that I added to my custom grid control. The methods behind the regions may be subject for a future article.

Listing 1: Class File for Custom Control derived from UltraWebGrid

I also suggest that you do not put all the grid related functionality in the user control that I will cover in the next paragraph. There are situations where you might want to use the pure grid control without all the other components included in the user control. Therefore it's quite reasonable to have the core functionality included in the custom grid class.

Create a user Control for the Custom Grid Control and all other Controls that are needed to manipulate Data in a Grid

In most cases a grid control needs additional controls such as a header row and a grid specific toolbar at the bottom. The best way to do this seems to be a user control that contains all the controls. This is the user control that I am using:

Listing 2: XHTML File of the User Control

Please note that the event handlers OnInitializeGrid and OnClickCellButton represent server side events. Your code behind must contain these methods for server side processing. The buttons defined in the toolbar container also have server side event handlers assigned:

  • OnGridButtonFindClick
  • OnGridButtonDeleteClick
  • OnGridButtonSaveClick

I should mention that I am using themes and that all assemblies are registered in the web.config. That is why the XHTML file looks so clean. In the following article I will refer to the UltraWebGrid as CustUltraWebGrid because I use a custom control that inherits from UltraWebGrid. If you don't use a custom control the grid is referred to as UltraWebGrid.

Make sure the Client can react on the custom add Record Button

New records shall be added to the grid by clicking the custom add button in the button bar at the bottom of the custom grid control instead of using the default buttons provided by the grid control.

Figure 4: Bar with command buttons

fig02.jpg

This button is used to add a new row to the highest level of the grid. The grid column buttons will be covered later. The image button named "ButtonAdd" in the user control has no server side event handler assigned because we do not want a postback being fired when this button gets pressed. However, we want that the client can react on the button. The client should also identify the name of the button and the name of the grid to which the button belongs, keeping in mind that a page can contain multiple grid user controls.

The following code needs to be added to the grid user control in the code behind (where this refers to the Grid Control in the code behind):

This will ensure that the event handler AddNewRow in the client script will be called whenever the button is clicked.

The event handler in the script expects that the name of the button includes the server id of the grid. We will need the server id of the grid control later to resolve the client id of the grid.

In this example the grid id includes underscores because I use grid names like GRID_GRIDMAIN_1. The client replaces underscores with the character x. Therefore all instances of underscore need to be replaced with x.

At this point the client will be able to react to the button being clicked, provided that the event handler is added to the client script. However, clicking the add button will still result in a postback. This is not the desired behavior because we want rows to be added by the client

Make sure the Image Button ID="ButtonAdd" does not cause a postback when clicked

The image button does not provide a property for deactivating auto postback. The desired result can be accomplished by adding the following attribute to the image button:

This is a kind of no operation for the button. It works for both ASP.NET buttons and ASP.NET image buttons. At this point the client can theoretically react to the button and will not cause a post back when the button is clicked.

Add the buttons to the bands of your grid control

Add the buttons to the band of your grid as usual. I suggest that you add and configure grid buttons programmatically. I assume that you are already familiar with the basic functionality of the UltraWebGrid.

Listing 5: Adding the Button Columns to the Grid Control

Enable the Client to add and delete Rows

All the JavaScript stuff we will be looking at later will not work unless the following mandatory settings are applied to the grid:

Listing 6: Configuring the Display Layout of the Grid Control

The CSOM of the grid knows the properties AllowAddNew and AllowDelete. Theoretically it should be possible to allow adding and deleting rows by setting AllowAddNew=1 and AllowDelete=1 in the client script, but this does not work as described. Therefore we need to get along with applying these settings on the server side.

Enable the Client to handle the Events needed to add and delete Rows on the Client Side using Grid Column Buttons.

Add the following client side events to your grid control.

Listing 7: Enabling the Client Side Events of the Grid Control

The type DisplayLayout is a nested Type of the type UltraWebGrid. You should take some time and look at dwhat it can do. You'll find an appropriate event handler for almost every event you can think of in connection with a grid control. In my opinion client side event handling is a real strength of the controls provided by Infragistics.

Take a breath and dive into JavaScript

If you want to make the UltraWebGrid work you cannot get along without JavaScript. If you have no experience with JavaScript you should definitely get used to it. I am an old client/server guy, but the things I am explaining here did not take much of my time.

Here are the scripts that are needed to support client side adding and deleting of grid rows. The following script must be added to the page in which the user control is embedded.

Note: By adding this code to the webpage which contains the grid user control, you get a collection named aGridId which contains the client ids of all grid controls that are included in the page. The variable bGridButtonDeleteClicked is used to determine whether the user clicked the delete button in the grid row or the delete key on the keyboard.

Pressing the delete key on the keyboard would - normally - instantly remove the row from the grid. We do not want this behavior and will therefore deactivate this behavior later.

Listing 9: The following code must be added to the user control - Function 1: AddNewRow The script tag is closed in the last of the 3 functions that need to be added.

Note: Please note that in my application the column containing the primary key of a record / row is named ID. Whenever a grid is submitted to the server it gets validated and processed by classes on the server. The ID value of new rows added to the client is always null. When the new rows get submitted to the server the ID value is temporarily set to -1. Therefore new rows that failed validation can be identified by the client based on their ID value. Rows that are processed by the server and pass validation show the primary key of the record in the ID column.

The function receives the name of the main button for adding new rows to band[0]. The name of the button includes the server id of the grid. If the server side id of the Grid is GRID_GRIDMAIN the name of the button is ButtonAddGRIDxGRIDMAIN. This is what you accomplished by adding the following event handler to the button.

In the foreach loop we are looking for a client id that contains the string GRIDxGRIDMAIN and use that id to refer to the grid object on the client.

Then we add a new row to band[0] and deactivate auto post back right after the row was added. This will prevent the page from being submitted to the server after adding a new row, which would be the default behavior of the UltraWebGrid.

Listing 11: The following code must be added to the user control - Function 2: BeforeRowDeleted

Remember: we allowed explicitly deleting rows by setting the following property of the grid:

By allowing deletions a user is able to simply press the delete button on the keyboard to discard a row from the grid. Although those deletions affect the client side only, this is considered unwanted behavior.

The event handler BeforeRowDeleted is hit before a row is deleted from the grid. If the code within that function returns the value true, the deletion is cancelled and nothing happens.

We just check if the deletion was caused by an authorized button. If not, the deletion is cancelled.

Listing 13: The following code must be added to the user control - Function 3: ClickCellButton

This function determines whether the row already exists or not. If the row has not been saved yet, it is identified by the ID value null or -1. If that is the case, all deletions will not be submitted to the server. The row will be deleted from the grid on the client side only. If a button is clicked that would normally redirect to a details page, this is also suppressed for rows that have not yet been saved.

It is also impossible to add child rows to parent rows that have not yet been saved. We then enter a switch block detecting which grid cell button was clicked.

Case 1 ButtonAdd

We do not allow adding child rows to parent rows that have not yet been saved. We then get the band of the row which fired the event and set the target band of the child row by incrementing the band of the parent row. Then we activate the parent row. This is important to do, otherwise nothing will happen. Finally we add the row to the target band. And cancel any pending post back which might be fired by the grid automatically after adding a new row.

Case 2 ButtonDelete

Deleting a row does normally require a post back because when a row gets deleted from the grid it disappears completely. It is impossible to access that row when processing the grid on the server in batch mode because deleted rows do not have the property DataChanged=DataChanged.Deleted assigned. They simply disappear completely.

In our application we do not use the standard server side event which is fired by the grid whenever a row gets deleted. We use the grid cell button instead of the delete key on the keyboard.

A postback is necessary whenever a row gets deleted that exists in the database. The server has to handle that. If a row gets deleted that exists on the client side only, we don't need that postback behavior and can delete that record on the client side only.

The functions described here need to be added to the user control as an embedded script. If you store the JavaScript in an external file it will not work because, at runtime, the path that you specify will be interpreted as a relative path to the path of the page which owns the control. This behavior is specific to user controls.

I have got a workaround for that, but this is not the place for dealing with that kind of problem.

Derive a new Page class from System.Web.UI.Page to change the persistence medium for page state

As I explained earlier, the full functionality of the UltraWebGrid requires that you enable the view state of the grid control. In order to prevent the page state - which can easily reach 100 Kilobytes and more depending on the complexity of the grid - from being submitted together with the page you must override the methods that are used to manage the page state.

I suggest that you create a new page class which is derived from System.Web.UI.Page.

This derived class can then be used for all pages that host large editable UltraWebGrids. Actually my class is derived from a navigation page. This page contains 90% of the functionality needed to create, manage and navigate web pages. The class PageNavigationBase itself is derived from System.Web.UI.Page.

Listing 14: Example of a derived Page Class that Changes the Persistence Medium of Page State

This class uses a class named PersistenceManager to load and save the view state. The PersistenceManager class manages how and where information gets stored that needs to be persisted beyond the lifecycle of a single page. In the addendum to this article I will only explain the two methods that are used to deal with page state. The class itself is too big and can do much more. I recommend that you make yourself familiar with this topic. The MSDN Library provides a couple of helpful articles on managing page state.

Conclusion

If you plan to use the UltraWebGrid effectively you need to understand the server-side object model and the client-side object model. If you use the UltraWebGrid out of the box you will most certainly experience performance and load problems and you might not get the user acceptance that you expected. These problems can be overcome by adding just a few small client scripts. The techniques that helped me most are explained in this article.

The grid as such should be brushed up by creating a custom inheritance control with all functionality that is typically needed for a grid control. The custom control should be embedded in a user control which contains complementary controls and the client scripts.

You should also use view state whenever you allow grid updating. Although it is possible to code around view state and use control state instead, it does not seem to be worth the effort in my opinion. When you use view state you must get your hands on managing page state as explained in this article. Otherwise the amount of data transmitted in view state might become a show stopper. It seems to be a good practice to create a class which acts as a wrapper for all HTTP-context related activities. This will enable you to change the media where information is physically persisted by just changing a switch in the configuration file without touching the code of your application.

The following thought is not for the ears of IT-controllers that are sitting on the bucks: It is better to add memory to your server or even spend money on a powerful sate server than giving up on much of the functionality provided by the UltraWebGrid or even make users upset by a poor performing application.

Addendum Supplementary Code Samples

The code in this addendum is not intended to serve as a copy and paste solution for your application. I am adding this code just to show you a simple context in which page state persistence management can be used.

My recommendation at this point is that you implement the persistence management in way that supports all 4 modes covered by this code sample. You will then be able to respond quickly to performance issues or system changes.

In my applications I even go a step further than that. All attempts to read or write to or from the cache or the session state are routed through methods of the persistence manager class, so I can even route session related object to the cache by just setting a switch in the configuration file.

The method in the next listing is a member of the type PersistenceManager. It is used for persisting the page state either in the file system, the session state, a state database on a SQL server or the cache. The persistence medium is configured in the web.config. The formatting looks a bit ugly due to the limited space available in the code box. You should copy the code into an editor for better readability.

Mode FileSystem: The page state is stored in the path defined in web.config. It can either be a path relative to the application root path or a physical path. The nested type PersistenceManager.Parameters has properties that provide the current path based on the current settings in web.config.

Mode SessionState: The page state is stored in the session object.

Mode Database: The page state is stored in a page state database on a SQL server. It is a simple database that has only one table and stored procedures for inserting, selecting and removing page state records.

Mode Cache: The page state is stored in the cache using the current parameters held by the type PersistenceManager.Parameters.

Listing 15: The method used to persist the page state in the media configured in web.config. This method is a member of type PersistenceManager

The following method is also a member of the type PersistenceManager. It reads the page state from the medium configured in web.config supporting the modes as described before.

Listing 16: The method used to load the page state from the media configured in web.config. This method is a member of type Persistence Manager.

The following class is a nested type of the type PersistenceManager. This type holds the current application settings for the persisting of the page state. The global object of the application (Global_asax) owns a static variable that holds the reference to the current instance of this type. Assuming that the name of the instance is PersistenceMediumParameters the parameters can be retrieved everywhere in the application by using the reference Global.PersistenceMediumParamters.Member.

Listing 17: The type used to manage the parameters of the currently selected mode for persisting page state

Listing 18: The enumeration used to define the persistence medium

This method is a member of the type PersistenceManager. It simply stores an object in the cache using the typical parameters.

Listing 19: The method used to persist the pagestate in the cache if defined in web.config. This method is a member of type PersistenceManager.

Listing 20: The enumeration used to define the cache operation

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Ralf Saha

Sorry, no bio is available

View complete profile here.

Other articles in this category


Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
jQuery Mobile ListView
In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
Exception Handling and .Net (A practical approach)
Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
JQuery Mobile Widgets Overview
An overview of widgets in jQuery Mobile.
Book Review: SignalR: Real-time Application Development
A book review of SignalR by Simone.

You might also be interested in the following related blog posts


HACK: debug modifications to the ASP.NET session read more
Top
 
 
 

Discussion


Subject Author Date
placeholder Poor Artticle Kazi Manzur Rashid 12/1/2007 6:38 PM
RE: Poor Artticle Ralf Saha 12/1/2007 8:01 PM
placeholder RE: RE: Poor Artticle Takashi Arikuma 7/3/2008 7:17 AM
RE: RE: RE: Poor Artticle Subnet Subnet 11/23/2009 1:30 PM
placeholder This is great stuff Lars Bergener 12/1/2007 9:05 PM
RE: This is great stuff Sonu Kapoor 12/2/2007 12:18 PM
placeholder Useful information in here Ronald Pofalla 12/2/2007 12:02 PM
Questions about the article Francisco Rodriguez 3/5/2008 12:22 PM

Please login to rate or to leave a comment.