Introduction to the use of TextChanged events in Asp.net TextBox

Source: Internet
Author: User

A brother in the blog asked me a question about how the dynamically created control loads the view status. He also mentioned calling the ProcessPostData method. Here I will use the TextChanged event of TextBox to describe how to load View data and how to trigger events.

Let's take a look at a demo:

The Code is as follows:

Running result

Now let's modify the value of the text box and click the button to submit the page to see what happened.

The TextChanged event of TextBox occurs at this time, and the running result

Now let's do nothing. Click Submit again to see what the effect will be:

This is why the TextChanged event of TextBox is not triggered at this time.

Do you know the conditions for triggering the TextChanged event of TextBox? Let's take a look at how the event is triggered today.

Here we will first look at the definition of TextBox:
Copy codeThe Code is as follows:
Public class TextBox: WebControl, IPostBackDataHandler, IEditableTextControl, ITextControl

Public interface IPostBackDataHandler
{
Bool LoadPostData (string postDataKey, NameValueCollection postCollection );
Void RaisePostDataChangedEvent ();
}
Public interface IEditableTextControl: ITextControl
{
Event EventHandler TextChanged;
}
Public interface ITextControl
{
String Text {get; set ;}
}


Here we mainly focus on the implementation of the IPostBackDataHandler interface,
Copy codeThe Code is as follows:
Protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection)
{
Base. ValidateEvent (postDataKey );
String text = this. Text;
String str2 = postCollection [postDataKey];
If (! This. ReadOnly &&! Text. Equals (str2, StringComparison. Ordinal ))
{
This. Text = str2;
Return true;
}
Return false;
}

Protected virtual void RaisePostDataChangedEvent ()
{
If (this. AutoPostBack &&! This. Page. IsPostBackEventControlRegistered)
{
This. Page. AutoPostBackControl = this;
If (this. CausesValidation)
{
This. Page. Validate (this. ValidationGroup );
}
}
This. OnTextChanged (EventArgs. Empty );
}



Here, the RaisePostDataChangedEvent method is better understood, mainly called the TextChanged event method, while the LoadPostData method can obtain the current textBox value (the old value string text = this. text;) and post new values (postCollection [postDataKey]). If the current textbox is not read-only and the new and old values are not the same, then the new value is assigned to the text attribute of the textbox, true is returned. If no, false is returned. Can we guess that the LoadPostData of textbox returns true before calling the RaisePostDataChangedEvent method.

In the previous asp.net Page event processing pipeline, we mentioned two special sections of code: Processing IPostBackDataHandler and processing IPostBackEventHandler.
First, let's take a look.
Copy codeThe Code is as follows:
This. OnInitComplete (EventArgs. Empty );

If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End InitComplete ");
}
If (this. IsPostBack)
{
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "Begin LoadState ");
}
This. LoadAllState ();
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End LoadState ");
This. Trace. Write ("aspx. page", "Begin ProcessPostData ");
}
This. ProcessPostData (this. _ requestValueCollection, true );
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End ProcessPostData ");
}
}
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "Begin PreLoad ");
}
This. OnPreLoad (EventArgs. Empty );



In this section, we are processing the IPostBackDataHandler interface after InitComplete and before PreLoad. Here we mainly use the LoadAllState and ProcessPostData methods.

First, we need to know what _ requestValueCollection is here. In fact, it is very simple. If it is post, it is mainly this. _ request. form (some of them are filtered out, such as filtering out _ VIEWSTATE ",__ EVENTTARGET). If a get request has a QueryString set, it is this. _ request. queryString is this. _ request. queryString.

The LoadAllState code is as follows:
Copy codeThe Code is as follows:
View Code
? Private void LoadAllState ()
{
Object obj2 = this. LoadPageStateFromPersistenceMedium ();
IDictionary first = null;
Pair second = null;
Pair pair2 = obj2 as Pair;
If (obj2! = Null)
{
First = pair2.First as IDictionary;
Second = pair2.Second as Pair;
}
If (first! = Null)
{
This. _ controlsRequiringPostBack = (ArrayList) first ["_ ControlsRequirePostBackKey _"];
If (this. _ registeredControlsRequiringControlState! = Null)
{
Foreach (Control control in (IEnumerable) this. _ registeredControlsRequiringControlState)
{
Control. LoadControlStateInternal (first [control. UniqueID]);
}
}
}
If (second! = Null)
{
String s = (string) second. First;
Int num = int. Parse (s, NumberFormatInfo. InvariantInfo );
This. _ fPageLayoutChanged = num! = This. GetTypeHashCode ();
If (! This. _ fPageLayoutChanged)
{
Base. LoadViewStateRecursive (second. Second );
}
}
}
Protected internal virtual object LoadPageStateFromPersistenceMedium ()
{
PageStatePersister pageStatePersister = this. PageStatePersister;
Try
{
PageStatePersister. Load ();
}
Catch (HttpException exception)
{
If (this. _ pageFlags [8])
{
Return null;
}
Exception. WebEventCode = 0 xbba;
Throw;
}
Return new Pair (pageStatePersister. ControlState, pageStatePersister. ViewState );
}
Internal void LoadChildViewStateByID (ArrayList childState)
{
Int count = childState. Count;
For (int I = 0; I <count; I + = 2)
{
String id = (string) childState [I];
Object savedState = childState [I + 1];
Control control = this. FindControl (id );
If (control! = Null)
{
Control. LoadViewStateRecursive (savedState );
}
Else
{
This. EnsureOccasionalFields ();
If (this. _ occasionalFields. ControlsViewState = null)
{
This. _ occasionalFields. ControlsViewState = new Hashtable ();
}
This. _ occasionalFields. ControlsViewState [id] = savedState;
}
}
}


The LoadAllState method is used to load the ControlState and ViewState data of each control. The data source is obtained through the LoadPageStateFromPersistenceMedium method. The data class is The ControlState data and ViewState data of each control in the last response.

Next, let's take a look at the ProcessPostData method,
Copy codeThe Code is as follows:
Private void ProcessPostData (NameValueCollection postData, bool fBeforeLoad)
{
If (this. _ changedPostDataConsumers = null)
{
This. _ changedPostDataConsumers = new ArrayList ();
}
If (postData! = Null)
{
Foreach (string str in postData)
{
If (str! = Null )&&! IsSystemPostField (str ))
{
Control control = this. FindControl (str );
If (control = null)
{
If (fBeforeLoad)
{
If (this. _ leftoverPostData = null)
{
This. _ leftoverPostData = new NameValueCollection ();
}
This. _ leftoverPostData. Add (str, null );
}
}
Else
{
IPostBackDataHandler postBackDataHandler = control. PostBackDataHandler;
If (postBackDataHandler = null)
{
If (control. PostBackEventHandler! = Null)
{
This. RegisterRequiresRaiseEvent (control. PostBackEventHandler );
}
}
Else
{
If (postBackDataHandler! = Null)
{
NameValueCollection postCollection = control. CalculateEffectiveValidateRequest ()? This. _ requestValueCollection: this. _ unvalidatedRequestValueCollection;
If (postBackDataHandler. LoadPostData (str, postCollection ))
{
This. _ changedPostDataConsumers. Add (control );
}
}
If (this. _ controlsRequiringPostBack! = Null)
{
This. _ controlsRequiringPostBack. Remove (str );
}
}
}
}
}
}
ArrayList list = null;
If (this. _ controlsRequiringPostBack! = Null)
{
Foreach (string str2 in this. _ controlsRequiringPostBack)
{
Control control2 = this. FindControl (str2 );
If (control2! = Null)
{
IPostBackDataHandler adapterInternal = control2.AdapterInternal as IPostBackDataHandler;
If (adapterInternal = null)
{
AdapterInternal = control2 as IPostBackDataHandler;
}
If (adapterInternal = null)
{
Throw new HttpException (SR. GetString ("Postback_ctrl_not_found", new object [] {str2 }));
}
NameValueCollection values2 = control2.CalculateEffectiveValidateRequest ()? This. _ requestValueCollection: this. _ unvalidatedRequestValueCollection;
If (adapterInternal. LoadPostData (str2, values2 ))
{
This. _ changedPostDataConsumers. Add (control2 );
}
}
Else if (fBeforeLoad)
{
If (list = null)
{
List = new ArrayList ();
}
List. Add (str2 );
}
}
This. _ controlsRequiringPostBack = list;
}
}

First, search for our Control based on the key of the created parameter NameValueCollection. Generally, the Control can be found, but the Control created by dynamic state in load cannot be found here. This method is divided into two parts, which are separated by ArrayList list = null. If the control is not found in the part, it is easy to process it. If you find out whether it is of the PostBackDataHandler type, if it is not and Its PostBackEventHandler is not empty, we will directly call its this. registerRequiresRaiseEvent (control. postBackEventHandler) method. If it is a PostBackEventHandler-type control, we call its LoadPostData method directly,
Copy codeThe Code is as follows:
If (postBackDataHandler. LoadPostData (str, postCollection ))
{
This. _ changedPostDataConsumers. Add (control );
}

Remove the control from the _ controlsRequiringPostBack set.
Copy codeThe Code is as follows:
If (this. _ controlsRequiringPostBack! = Null)
{
This. _ controlsRequiringPostBack. Remove (str );
}

The second part of this method is to traverse the set in controlsRequiringPostBack. Its processing method is similar to the previous one, but if the control id is not found, it is recorded.
Copy codeThe Code is as follows:
Else if (fBeforeLoad)
{
If (list = null)
{
List = new ArrayList ();
}
List. Add (str2 );
}

By default, _ controlsRequiringPostBack contains dynamically created controls. Let's talk about this set here,

The setting of controlsRequiringPostBack is the code in the LoadAllState method:

This. _ controlsRequiringPostBack = (ArrayList) first ["_ ControlsRequirePostBackKey _"]; SaveAllState (when LoadAllState (data loading state) exists ), there is such a code in the SaveAllState:

Dictionary. Add ("_ ControlsRequirePostBackKey _", this. _ registeredControlsThatRequirePostBack );

The _ registeredControlsThatRequirePostBack set is defined in the RegisterRequiresPostBack method.
Copy codeThe Code is as follows:
Public void RegisterRequiresPostBack (Control control)
{
If (! (Control is IPostBackDataHandler )&&! (Control. AdapterInternal is IPostBackDataHandler ))
{
Throw new HttpException (SR. GetString ("Ctrl_not_data_handler "));
}
If (this. _ registeredControlsThatRequirePostBack = null)
{
This. _ registeredControlsThatRequirePostBack = new ArrayList ();
}
This. _ registeredControlsThatRequirePostBack. Add (control. UniqueID );
}

In short, the controls dynamically added here cannot load data, but other default controls can be processed here.

Now let's take a look at how controls are added. In the Control class, the AddedControl method is used to add controls:
Copy codeThe Code is as follows:
Protected internal virtual void AddedControl (Control control Control, int index)
{
If (control. OwnerControl! = Null)
{
Throw new InvalidOperationException (SR. GetString ("Substitution_NotAllowed "));
}
If (control. _ parent! = Null)
{
Control. _ parent. Controls. Remove (control );
}
Control. _ parent = this;
Control. _ page = this. Page;
Control. flags. Clear (0x20000 );
Control namingContainer = this. flags [0x80]? This: this. _ namingContainer;
If (namingContainer! = Null)
{
Control. UpdateNamingContainer (namingContainer );
If (control. _ id = null )&&! Control. flags [0x40])
{
Control. GenerateAutomaticID ();
}
Else if (control. _ id! = Null) | (control. _ controls! = Null ))
{
NamingContainer. DirtyNameTable ();
}
}
If (this. _ controlState> = ControlState. ChildrenInitialized)
{
Control. InitRecursive (namingContainer );
If (control. _ controlState> = ControlState. Initialized) & (control. RareFields! = Null) & control. RareFields. RequiredControlState)
{
This. Page. RegisterRequiresControlState (control );
}
If (this. _ controlState> = ControlState. ViewStateLoaded)
{
Object savedState = null;
If (this. _ occasionalFields! = Null) & (this. _ occasionalFields. ControlsViewState! = Null ))
{
SavedState = this. _ occasionalFields. ControlsViewState [index];
If (this. LoadViewStateByID)
{
Control. EnsureID ();
SavedState = this. _ occasionalFields. ControlsViewState [control. ID];
This. _ occasionalFields. ControlsViewState. Remove (control. ID );
}
Else
{
SavedState = this. _ occasionalFields. ControlsViewState [index];
This. _ occasionalFields. ControlsViewState. Remove (index );
}
}
Control. LoadViewStateRecursive (savedState );
If (this. _ controlState> = ControlState. Loaded)
{
Control. LoadRecursive ();
If (this. _ controlState> = ControlState. PreRendered)
{
Control. PreRenderRecursiveInternal ();
}
}
}
}
}

In this method, call this. page. registerRequiresControlState (control) and control. loadViewStateRecursive (savedState) method. One is responsible for ControlState and the other is responsible for loading ViewState data. When we send 2nd and 3 post requests here, when the textboxt control is created in load, its existing control status and view status are loaded.
Now let's take a look at the section with code processing IPostBackEventHandler in ProcessRequestMain:
Copy codeThe Code is as follows:
This. LoadRecursive ();
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End Load ");
}
If (this. IsPostBack)
{
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "Begin ProcessPostData Second Try ");
}
This. ProcessPostData (this. _ leftoverPostData, false );
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End ProcessPostData Second Try ");
This. Trace. Write ("aspx. page", "Begin Raise ChangedEvents ");
}
This. RaiseChangedEvents ();
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End Raise ChangedEvents ");
This. Trace. Write ("aspx. page", "Begin Raise PostBackEvent ");
}
This. RaisePostBackEvent (this. _ requestValueCollection );
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "End Raise PostBackEvent ");
}
}
If (context. TraceIsEnabled)
{
This. Trace. Write ("aspx. page", "Begin LoadComplete ");
}
This. OnLoadComplete (EventArgs. Empty );

First, let's take a look at the _ leftoverPostData set. It is an id set of the control that was not found when the ProcessPostData method was called previously. The control can be found here. The execution route is mainly the following part of the ArrayList list = null sentence.
Copy codeThe Code is as follows:
If (adapterInternal. LoadPostData (str2, values2 ))
{
This. _ changedPostDataConsumers. Add (control2 );
}

Here, the second call to the ProcessPostData method is mainly to handle the issue of dynamically creating controls events.

Here let's take a look at the RaiseChangedEvents method:
Copy codeThe Code is as follows:
Internal void RaiseChangedEvents ()
{
If (this. _ changedPostDataConsumers! = Null)
{
For (int I = 0; I <this. _ changedPostDataConsumers. Count; I ++)
{
Control control = (Control) this. _ changedPostDataConsumers [I];
If (control! = Null)
{
IPostBackDataHandler postBackDataHandler = control. PostBackDataHandler;
If (control = null) | control. IsDescendentOf (this) & (control! = Null) & (control. PostBackDataHandler! = Null )))
{
PostBackDataHandler. RaisePostDataChangedEvent ();
}
}
}
}
}

I think you should be clear about the execution of the TextChanged event of TextBox here. The RaisePostBackEvent method is not mentioned. You will understand the code,
Copy codeThe Code is as follows:
Private void RaisePostBackEvent (NameValueCollection postData)
{
If (this. _ registeredControlThatRequireRaiseEvent! = Null)
{
This. RaisePostBackEvent (this. _ registeredControlThatRequireRaiseEvent, null );
}
Else
{
String str = postData ["_ EVENTTARGET"];
Bool flag =! String. IsNullOrEmpty (str );
If (flag | (this. AutoPostBackControl! = Null ))
{
Control control = null;
If (flag)
{
Control = this. FindControl (str );
}
If (control! = Null) & (control. PostBackEventHandler! = Null ))
{
String eventArgument = postData ["_ EVENTARGUMENT"];
This. RaisePostBackEvent (control. PostBackEventHandler, eventArgument );
}
}
Else
{
This. Validate ();
}
}
}

Here, we recall that the State information of the control is generally saved through the SaveAllState method, while the load state information is the LoadAllState method after InitComplete and before PreLoad, the loaded data is the data saved by the SaveAllState method in the previous request. After loading the data, the ProcessPostData method is called to process the post data, status data is loaded when a dynamically added control is added for the second and later requests. It is said that the dynamically added control loads its status data when it is added. After loading and before LoadComplete, we are dealing with the control event calling problem. Here we call ProcessPostData again to process dynamically created controls and post data, then, call the RaiseChangedEvents and RaisePostBackEvent methods to call events in IPostBackDataHandler and IPostBackEventHandler.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.