Detect Control Changes
Use server-side events to detect state changes.
October 30, 2009
CoreCoder
LANGUAGES: C#
TECHNOLOGIES: InputControls | 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 andRadioButtonList, represent an effective way to group together multiple dataitems and choices. Typically, the user selects one element or more and postsback. On the server, you easily can retrieve the checked items and proceed withfurther processing.
In many real-world cases, though, this information aloneis not enough. You also might need to know what items have changed since lasttime and whether any selections have changed. For example, suppose you have aform with an employee's personal data. The form contains a checkbox list inwhich each element represents a possible skill of that employee. Each time theform is processed and updated, you need to know which skills are selected atthe time. More importantly, you want to know if any skills have been added orremoved since the last time the form was posted.
Detecting selection changes in ASP.NET list controls isonly one example of a more general problem: detecting property changes in inputcontrols, which are hosted by HTML forms. As such, the ability to detect propertychanges is important for all input controls, not only list controls.
Initiate Postback Events
A handful of Web and HTML input controls fire server-sideevents whenever their internal status changes. The way these events are namedis not consistent, but the feature is common to HTML input controls as well aslist and Web controls. Input controls lack the ability to initiate postbackevents to let server-side code run. Typically, postback events are generatedonly by button and link controls in addition to all controls that support theAutoPostback property.
Many input controls support the AutoPostback Booleanproperty, but this property rarely is effective because it posts to the serverevery time the selection changes. Normally, that would be too often. What youreally need is a way to submit changes to the server independent from clickingon the input controls. Once on the server, though, you should be able toprocess changes to the controls only if the user actually modified them sincelast time. For example, you might want to process the record associated with aselected 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 postbackevent moves you to the server. Second, the property-change event gives you achance to process changes on the server.
Input controls expose a server-change event, but the codeexecutes on the server only with the next postback - whichever generates it andwhenever it occurs. For example, the TextBox control raises a TextChanged eventwhen the content of the buffer changes between two consecutive posts to theserver.
How Controls Handle State Changes
Consider the TextBox control and what happens when thepage containing the control posts back. The page author defines the controllike this:
OnTextChanged ="NotesChanged" /> The user types some text and submits the page. On theserver, the ASP.NET runtime restores the original state of the page, then entersinto a phase named Raise ChangedEvents. At this stage, each page control thatimplements the IPostBackDataHandler interface is given a chance to update itscurrent state with data posted by the client. The ASP.NET runtime invokesLoadPostData - one of the two methods defined by the IPostBackDataHandlerinterface - sequentially on each control that implements this interface: public bool LoadPostData(string postDataKey, NameValueCollection postCollection); The first argument of LoadPostData is a string thatidentifies the control; the second is a name-value collection that contains theposted data. You use the first string as a key to access the posted valuestored in the collection. The posted value refers to one particular property onthe control - the one you would have retrieved by using Request.Form in old ASPapplications. The value is Text for textboxes and Checked for checkboxes. Controls that implement the IPostBackDataHandler interfaceuse a piece of boilerplate code to implement the LoadPostData method.Basically, the method updates the key property of the control with the postedvalue. The pseudo-code in Figure 1 shows how LoadPostData works for the TextBoxcontrol. private virtualbool System.Web.UI.IPostBackDataHandler.LoadPostData( string postDataKey,NameValueCollection postColl) { string oldValue; string postedValue; // Restored from viewstate (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 TextBoxcontrol updates the Text property with the posted value. LoadPostData returns True if the state of the controlchanges as a result of the postback - that is, if the user generated a newstate for the control. For this infrastructure to work, the value of the servercontrol's UniqueID property must be assigned to the HTML element's Nameattribute. Otherwise, the ASP.NET runtime will not be able to handle postbackdata for that control. The ASP.NET runtime tracks all the controls that returnTrue to LoadPostData and invokes the RaisePostDataChangedEvent method for eachof them. This code snippet reports what that method does for the TextBoxcontrol: // System.Web.UI.IPostBackDataHandlervoid RaisePostDataChangedEvent(){ this.OnTextChanged(EventArgs.Empty); } As you can see, the TextBox control simply executes theuser-defined server-side handler for the TextChanged event. In light of thecontrol 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 astate-change handler, the order of the controls in the page determines theorder in which handlers actually execute. All state-change handlers areprocessed before the postback handler code runs. Figure 2 demonstrates this.
Figure 2. State-change events are processed before the postback eventcode executes. Notice that state-change notifications are raised afterthe controls' state has been restored from view state and finally updated usingposted data. Explore State-Change Events In Figure 3, you can see the list of all server controlsthat implement the IPostBackDataHandler interface. The second column of thetable shows the property monitored; the third column details the correspondingstate-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 theIPostBackDataHandler 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 theLoadPostData method. Their implementation of the RaisePostDataChangedEventmethod is void. The reason is that the controls feature properties to trackacross 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 dependingon the settings you define. In the case of a dropdown list, it monitors theSelectedIndex property for changes; in the case of a list box, the controldetects changes in the Items collection. Each item in the collection isrepresented by a ListItem object. The HtmlSelect control looks after theSelected property of the various ListItem elements. Notice that, for thestate-change events to work correctly, the controls must have the view-statefeature enabled. If the view state is disabled, there is no way to compare theposted value with the previous value of the control. No exception is everraised, but the result you get is unreliable because the posted value always iscompared with the default value of the monitored property rather than with thevalue 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 eventsignals that the value in the control has changed by turning the backgroundcolor to yellow. What you need to do in response to a state-change eventdepends in particular on the nature of the application, but this code providesa 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 isrestored to the default color in the Page_Load event.
Figure 4. In this page, you know the values of the input controls arechanged because the code turns the background color of the fields to yellow. The events are raised only if the monitored propertychanges during the client activity. When the page posts back, the comparison isdone between the value stored in the view state and the current value posted bythe client. Notice that when these state-change events occur, the programmerreceives no additional information about what has changed. The programmer isnotified only that the value for the monitored property has changed. This isnot an issue for standalone controls such as the TextBox, but it is an issuefor list controls such as CheckBoxList and RadioButtonList. In these cases, yousimply don't know what items have changed since last time. A possibleworkaround would be to store the indexes of the checked items in a page-specificentry in the view state and compare it with a list of checked items within theSelectedIndexChanged event handler. Alternately, as long as the applicationlogic allows it, you could disable the checked items so that they are the onlyitems changed at each successive postback. This approach works, however, onlyin situations in which you use the checkbox list to flag out items. Detecting state changes on the server, in the context of apostback event, is a critical programming aspect in several applications. Forexample, for applications that need to accomplish heavy updating tasks, knowingwhether a given field has been updated since the last round trip can be asignificant optimization. The state-change events don't initiate a postbackthemselves, but if the controls have the AutoPostback property set to True,each state change posts the page back automatically. Not all controls exposethe AutoPostback property; it is a specific feature of list controls. You setit to True only if the server needs to capture the selection as soon as it ismade to populate other controls. The sample code in thisarticle is available for download. Dino Esposito is a trainer and consultant whospecializes in ASP.NET, ADO.NET, and XML. Author of Building Web Solutions with ASP.NET andADO.NET and the upcoming Programming ASP.NET (Microsoft Press), he also is a co-founderof http://www.VB2TheMax.com.E-mail him at mailto:[email protected]. Tell us what you think! Please send any comments aboutthis article to [email protected] include the article title and author.
About the Author
You May Also Like