Detect Control Changes

Use server-side events to detect state changes.

CoreCoder

LANGUAGES: C#

TECHNOLOGIES: Input Controls | Postback | IPostBackDataHandler | State Change

 

Detect Control Changes

Use server-side events to detect state changes.

 

By Dino Esposito

 

ASP.NET list controls, such as CheckBoxList and RadioButtonList, represent an effective way to group together multiple data items and choices. Typically, the user selects one element or more and posts back. On the server, you easily can retrieve the checked items and proceed with further processing.

 

In many real-world cases, though, this information alone is not enough. You also might need to know what items have changed since last time and whether any selections have changed. For example, suppose you have a form with an employee's personal data. The form contains a checkbox list in which each element represents a possible skill of that employee. Each time the form is processed and updated, you need to know which skills are selected at the time. More importantly, you want to know if any skills have been added or removed since the last time the form was posted.

 

Detecting selection changes in ASP.NET list controls is only one example of a more general problem: detecting property changes in input controls, which are hosted by HTML forms. As such, the ability to detect property changes is important for all input controls, not only list controls.

 

Initiate Postback Events

A handful of Web and HTML input controls fire server-side events whenever their internal status changes. The way these events are named is not consistent, but the feature is common to HTML input controls as well as list and Web controls. Input controls lack the ability to initiate postback events to let server-side code run. Typically, postback events are generated only by button and link controls in addition to all controls that support the AutoPostback property.

 

Many input controls support the AutoPostback Boolean property, but this property rarely is effective because it posts to the server every time the selection changes. Normally, that would be too often. What you really need is a way to submit changes to the server independent from clicking on the input controls. Once on the server, though, you should be able to process changes to the controls only if the user actually modified them since last time. For example, you might want to process the record associated with a selected checkbox only if the selection took place during the last round trip. For this to happen, you need a double level of events. First, the postback event moves you to the server. Second, the property-change event gives you a chance to process changes on the server.

 

Input controls expose a server-change event, but the code executes on the server only with the next postback - whichever generates it and whenever it occurs. For example, the TextBox control raises a TextChanged event when the content of the buffer changes between two consecutive posts to the server.

 

How Controls Handle State Changes

Consider the TextBox control and what happens when the page containing the control posts back. The page author defines the control like this:

 

OnTextChanged ="NotesChanged" />

 

The user types some text and submits the page. On the server, the ASP.NET runtime restores the original state of the page, then enters into a phase named Raise ChangedEvents. At this stage, each page control that implements the IPostBackDataHandler interface is given a chance to update its current state with data posted by the client. The ASP.NET runtime invokes LoadPostData - one of the two methods defined by the IPostBackDataHandler interface - sequentially on each control that implements this interface:

 

public bool LoadPostData(

string postDataKey,

NameValueCollection postCollection);

 

The first argument of LoadPostData is a string that identifies the control; the second is a name-value collection that contains the posted data. You use the first string as a key to access the posted value stored in the collection. The posted value refers to one particular property on the control - the one you would have retrieved by using Request.Form in old ASP applications. The value is Text for textboxes and Checked for checkboxes.

 

Controls that implement the IPostBackDataHandler interface use a piece of boilerplate code to implement the LoadPostData method. Basically, the method updates the key property of the control with the posted value. The pseudo-code in Figure 1 shows how LoadPostData works for the TextBox control.

 

private virtual

bool System.Web.UI.IPostBackDataHandler.LoadPostData(

     string postDataKey, NameValueCollection postColl)

{

     string oldValue;

     string postedValue;

 

     // Restored from view state (must be enabled)

     oldValue = this.Text;

     postedValue = postColl[postDataKey];

     if (oldValue != postedValue)

      {

  this.Text = postedValue;

  return true;

     }

    

     return false;

}

Figure 1. The LoadPostData method for the TextBox control updates the Text property with the posted value.

 

LoadPostData returns True if the state of the control changes as a result of the postback - that is, if the user generated a new state for the control. For this infrastructure to work, the value of the server control's UniqueID property must be assigned to the HTML element's Name attribute. Otherwise, the ASP.NET runtime will not be able to handle postback data for that control.

 

The ASP.NET runtime tracks all the controls that return True to LoadPostData and invokes the RaisePostDataChangedEvent method for each of them. This code snippet reports what that method does for the TextBox control:

 

// System.Web.UI.IPostBackDataHandler

void RaisePostDataChangedEvent()

{

    this.OnTextChanged(EventArgs.Empty);

}

 

As you can see, the TextBox control simply executes the user-defined server-side handler for the TextChanged event. In light of the control declaration shown previously, the NotesChanged method executes:

 

void NotesChanged(object sender, EventArgs e)

{

      Trace.Warn("Notes field has been changed");

}

 

If the page contains multiple input controls, each with a state-change handler, the order of the controls in the page determines the order in which handlers actually execute. All state-change handlers are processed before the postback handler code runs. Figure 2 demonstrates this.

 


Figure 2. State-change events are processed before the postback event code executes.

 

Notice that state-change notifications are raised after the controls' state has been restored from view state and finally updated using posted data.

 

Explore State-Change Events

In Figure 3, you can see the list of all server controls that implement the IPostBackDataHandler interface. The second column of the table shows the property monitored; the third column details the corresponding state-change event.

 

Class

Property

State-Change Event

CheckBox

Checked

CheckedChanged

CheckBoxList

SelectedIndex

SelectedIndexChanged

DropDownList

Selected

SelectedIndexChanged

HtmlInputCheckBox

Checked

ServerChange

HtmlInputHidden

Value

ServerChange

HtmlInputRadioButton

Checked

ServerChange

HtmlInputText

Value

ServerChange

HtmlSelect

SelectedIndex

ServerChange

HtmlTextArea

Value

ServerChange

ListBox

SelectedIndex

ServerChange

RadioButtonList

SelectedIndex

SelectedIndexChanged

TextBox

Text

TextChanged

Figure 3. These server controls implement the IPostBackDataHandler interface.

 

Three additional controls, not listed in Figure 3, implement the IPostBackDataHandler interface: HtmlInputFile, HtmlInputImage, and ImageButton. These all provide a significant implementation only for the LoadPostData method. Their implementation of the RaisePostDataChangedEvent method is void. The reason is that the controls feature properties to track across page requests but have no architectural need to signal a state change. The HtmlSelect control can work either as a dropdown list or list box depending on the settings you define. In the case of a dropdown list, it monitors the SelectedIndex property for changes; in the case of a list box, the control detects changes in the Items collection. Each item in the collection is represented by a ListItem object. The HtmlSelect control looks after the Selected property of the various ListItem elements. Notice that, for the state-change events to work correctly, the controls must have the view-state feature enabled. If the view state is disabled, there is no way to compare the posted value with the previous value of the control. No exception is ever raised, but the result you get is unreliable because the posted value always is compared with the default value of the monitored property rather than with the value held when the previous post occurred.

 

Put it All Together

The page shown in Figure 4 contains a few input controls, each with a registered state-change event handler. The code in the event signals that the value in the control has changed by turning the background color to yellow. What you need to do in response to a state-change event depends in particular on the nature of the application, but this code provides a starting point:

 

public void TextChanged(object sender, EventArgs e) {

    ((WebControl) sender).BackColor = Color.Yellow;

}

 

public void NotesChanged(object sender, EventArgs e) {

   HtmlTextArea area = ((HtmlTextArea) sender);

   area.Style["background-color"] = "yellow";

}

 

public void IndexChanged(object sender, EventArgs e) {

    ((WebControl) sender).BackColor = Color.Yellow;  

}

 

The background color then is restored to the default color in the Page_Load event.

 


Figure 4. In this page, you know the values of the input controls are changed because the code turns the background color of the fields to yellow.

 

The events are raised only if the monitored property changes during the client activity. When the page posts back, the comparison is done between the value stored in the view state and the current value posted by the client. Notice that when these state-change events occur, the programmer receives no additional information about what has changed. The programmer is notified only that the value for the monitored property has changed. This is not an issue for standalone controls such as the TextBox, but it is an issue for list controls such as CheckBoxList and RadioButtonList. In these cases, you simply don't know what items have changed since last time. A possible workaround would be to store the indexes of the checked items in a page-specific entry in the view state and compare it with a list of checked items within the SelectedIndexChanged event handler. Alternately, as long as the application logic allows it, you could disable the checked items so that they are the only items changed at each successive postback. This approach works, however, only in situations in which you use the checkbox list to flag out items.

 

Detecting state changes on the server, in the context of a postback event, is a critical programming aspect in several applications. For example, for applications that need to accomplish heavy updating tasks, knowing whether a given field has been updated since the last round trip can be a significant optimization. The state-change events don't initiate a postback themselves, but if the controls have the AutoPostback property set to True, each state change posts the page back automatically. Not all controls expose the AutoPostback property; it is a specific feature of list controls. You set it to True only if the server needs to capture the selection as soon as it is made to populate other controls.

 

The sample code in this article is available for download.

 

Dino Esposito is a trainer and consultant who specializes in ASP.NET, ADO.NET, and XML. Author of Building Web Solutions with ASP.NET and ADO.NET and the upcoming Programming ASP.NET (Microsoft Press), he also is a co-founder of http://www.VB2TheMax.com. E-mail him at mailto:[email protected].

 

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish