In ASP.NET, the value change related event will be triggered at server side per server control when the value of such a server control (form field) is changed upon postback.

In most cases, a group of form fields are correlated with each other and typically correspond to member data in a business object. Thus developers need to check if such a group of form fields has changed as a whole. Unfortunately, the .NET Framework (1.x, 2.0) doesn’t offer an effective solution. This article will present an elegant technique to solve this problem and it can even go further so that you can know which field has changed.

Knowing whether a group of form fields have changed and which fields have changed can be beneficial in many ASP.NET Web applications. Let me list a few of those benefits:

  • Provide a reliable server-side method to detect changes on form fields as a whole and only changed groups of form data will be persisted into the database, thus avoiding unnecessary database update via stored procedures or embedded SQL. In the worst case, it bears a marginal overhead imposed by change-checking logic.
  • More interestingly, you can build an embedded UPDATE SQL statement on the fly to update only changed columns if any change of form fields happens and form data corresponds to columns of a table. Thus, this technique can reduce coding and possibly improve performance.
  • This technique enriches the user experience and workflow of data processing in Web Forms. You can update multiple table records or groups of form data in batches with one click of a button. Since this technique avoids unnecessary database updates, it doesn’t degrade overall performance. More significantly, you can reduce page postbacks. In contrast, ASP.NET 2.0’s support out of the box either updates all records with one button click or you have to associate an update button with each record (typically seen in an iterative control such as Repeater or DataList, and so on).

Installing and Compiling Sample Code

I based the code in this article on the .NET Framework 1.1 and Visual Basic .NET. You could easily port the code to .NET Framework 1.0 and 2.0 by recompiling it. The .zip file contains two projects: xControls implements the logic to detect form data changes, and DetectFormValueChange is a small Web project for demo purposes. Copy the code into a virtual directory named DetectFormValueChange and open the TestChange.aspx Web Form to run the demo.

Implementation

Developers might try to hook the value change event handler per server control, but that leads to code that is tedious, messy, and worse-not reusable. To solve this problem, you’ll use a technique called subclassing that enables developers to extend the functionalities of server controls by supplying an interface, hooking up an event handler, etc. The subclassed code that results will be well-encapsulated and will offer good reusability. You’ll use three straightforward steps to implement this server-side technique.

Subclassing enables developers to extend functionalities of server controls such as supplying an interface, hooking up an event handler, etc. The subclassed code that results will be well-encapsulated and will offer good reusability.

Identify server controls that can hold form data and extend them with a common interface property called FormGroup that allows you to distinguish different groups of form fields.

Hook up the value change event handler to save form data change information. When something changes a form field value, this special handler executes and stores the change information for later query.

Execute implementation logic to determine how change information of the form fields is stored and queried. The logic is static functions and encapsulated in a separate class.

Step 1: Identify Server Control and Extend Common Interface

Identify all server controls that present data for users to edit such as TextBox, DropDownList, RadioButton, CheckBox, ListBox, RadioButtonList, CheckButtonList, etc. You should also include Literal and Label because developers may use them to display some immutable fields such as table primary keys. You could expand this list even further, depending on the developer. For example, ASP.NET 2.0 comes with a server control called HiddenField that you might include. Developers can also include customized controls to this list following the changes below. After identifying the server controls, they need to be extended and implement the following interface:

Public Interface IFormGroup
    Property FormGroup() As String
End Interface

IFormGroup supplies the controls with a common interface other than Control and serves to single out these fields within a container easily. The FormGroup property can be of type integer, but to make it more generic it is of type string. FormGroup typically corresponds to a key column of a table record. The value of FormGroup should be unique for different groups of form data. You implement FormGroup following the same convention as other server control properties.

Public Property FormGroup() As String
         Implements IFormGroup.FormGroup
    Get
        Dim obj As Object
        obj = ViewState("FormGroup")
        If obj Is Nothing Then
            Return Nothing
        Else
            Return CType(obj, String)
        End If
    End Get
    Set(ByVal Value As String)
        ViewState("FormGroup") = Value
    End Set
End Property

Step 2: Hook up Value Change Event Handler

When a form field changes, the postback value change event handler, if any, will be triggered. You should exclude Literal and Label since these two controls present data for view only. The event name and argument might be different for these controls, but the body of the event handler remains unchanged, just calling a static function called AddChangedField. AddChangedField is a supportive function responsible for recording the value change information of this control associated with FormGroup. More details on supportive functions come in the third step. For simplicity I’ll use TextBox as an example.

Private Sub ValueChanged(ByVal sender As Object, _
      ByVal args As EventArgs) Handles _
      MyBase.TextChanged
xControls.FormGroup.AddChangedField(FormGroup, ID)
End Sub

Step 3: Implement Logic to Store and Query Change Information of Form Data

A number of functions work together to realize this technique. All of them are static and thus you can bundle them into a separate FormGroup class, as seen in Listing 1. AddChangedField serves to record field changes and its associated FormGroup. Note that if value of FormGroup is missing, such as Nothing in VB.NET, change information will get lost since form field must belong to a group. Other similar methods include:

  • HasFormGroupChanged Checks whether a form group is changed
  • HasFormGroupFieldChanged Checks if a specific form field in a form group is changed
  • FindFormGroupFields Is overloaded and serves to find out all server controls within a container that belong to the same form group
  • FindChangedFormGroupFields Singles out all changed fields in a group of form fields.

So where does the changed information get stored? A NameValueCollection instance records changes of all fields in the global cache Http Context Items as a central storage. Http Context Items is a page-level cache and accessible to all server controls. The key called $#!@FormGroup is special and unique and should not be overridden. For each server control its key is computed by BuildControlKey based on FormGroup and a Control ID.

Demo

To illustrate this technique I’ll create a simple Web Form called TestChange.aspx. Users can edit this form and then submit form data. It will display IDs of changed form fields, as seen in Figure 1. A Panel serves as the container of all form fields. The code calls the FindChangedFormGroupFields in the Submit button’s Click event to extract the ID of the changed form field. Very interestingly, if you change some fields and submit the form, IDs of changed fields will then be displayed in red. After you click Submit, nothing is immediately shown since no change takes place this time. In the subsequent two sections I will provide pseudo code on two sample applications. One will update form data upon change. The other will build a SQL UPDATE statement on the fly. You can easily apply the skills I present in this article to enterprise Web applications so as to increase performance and enrich the flow of your Web sites.

Figure 1: This simple Web page vividly reveals how a change of form field is detected.

Application 1: Update Form Data upon Change

Consider an iterative control such as Repeater that contains multiple groups of form fields, each RepeaterItem will serve as the container of one group of form fields. Bind FormGroup to the first server control within a repeater item, which might be an invisible Literal if your user does not need to see it. Listing 2 shows how to iterate a Repeater to update a changed group of form fields within Repeater items.

Application 2: Build a Dynamical SQL UPDATE Statement

If a group of form fields corresponds to a data table record, you can build a dynamic SQL UPDATE statement based on change information from the data fields. To make it work, you need to know the SQL parameter name, the SQL parameter type, and the value. For simplicity, assume Control ID is the same as column name. Next, add two other properties, SqlType and SqlValue, to the common interface to supply the SQL parameter type and value. Listing 3 shows how to build a SQL UPDATE statement using SqlParameter. There are other ways to know about parameter name and value such as reflection, but you cannot build a dynamic SQL UPDATE statement without knowing which field has changed.

You can easily apply the skills I present in this article to enterprise Web applications so as to increase performance and enrich the flow of your Web sites.

Some developers may argue that embedded SQL may hurt performance compared with stored procedures. Updating all columns of a table record using embedded SQL or stored procedures may incur an unnecessary database retrieval to restore state upon page postback, and updating many columns using stored procedure may also decrease performance compared to updating only few changed columns using dynamically-built embedded SQL UPDATE. Without a doubt, dynamically built embedded SQL will improve performance if you only use embedded SQL. You must choose whether to use embedded SQL or stored procedures.

Conclusion

This article presents a reliable and effective server-side technique to detect changes in form fields as a whole, which can help to avoid unnecessary data access, reduce coding, and enrich the user experience. I hope you will enjoy the technique and use it in your applications. If you want to use it in ASP.NET 2.0, simply recompile the code in Visual Studio 2005.