viewstate的作用

1         ViewState机制是什么?

ViewState机制是asp.net中对同一个Page的多次请求(PostBack)之间维持Page及控件状态的一种机制。在WebForm中每次请求完,Page对象都会被释放,对同一个Page的多次请求之间的状态信息,如何进行维护呢?WebForm中,每次请求都会存在客户端和服务器之间的一个交互。如果请求完成之后将一些信息传回到客户端,下次请求的时候客户端再将这些状态信息提交给服务器,服务器端对这些信息使用和处理,再将这些信息传回给客户端。这样是不是就可以对同一个Page的多次请求(PostBack)之间维持状态了。对这就是ViewState的基本工作模式。ViewState的设计目的主要就是为了将必要的信息持久化在页面中。这样通过ViewState在页面回传的过程中保存状态值,使原本没有“记忆”的Http协议变得有“记忆”起来。

2         ViewState机制如何工作?

下面我们看看ViewState机制是如何具体的工作的。

2.1 客户端:

我们先从客户端看起,在客户端的HTML源代码中我们可以看到下面的代码

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"

value="/wEPDwULLTE0MTAzNDUwNThkZKr77J2uy7fatyBou8PocG80X4Jt" />

这个就是ViewState在客户端的保存形式,它保存在一个ID为__VIEWSTATE的Hidden中,它的Value是使用Base64编码后的字符串。这个字符串实际上是一个对象(Pair类型)序列化之后的结果。这个对象保存了整个页面的控件树的ViewState。可以使用一些工具将这个字符串进行解码查看其内容,比如ViewStateDecoder,ViewStateAnalyzer。

2.2 服务器端:

在服务器端和ViewState机制密切相关的有三个类Page,Control,StateBag。他们3者的关系如下图所示:

viewstate的作用
图1

Page继承自Control,Control和StateBag是聚合关系,在Control中有一个StateBag的实例ViewState。这三个类互相协作完成ViewState机制的大概过程如下。Page对客户端请求进行处理,在处理的过程中先是将客户端提交的_VIEWSTATE反序列化为对象,调用Control的相关方法给所有的控件装载数据,这些数据是上次请求结束后控件的状态数据。在之后的一些事件中这些状态数据可能被修改。在请求结束之前调用Control的相关方法得到所有控件的被修改过的状态数据,之后Page将其进行序列化,并返回给客户端。在Control中又具体调用StateBag类的方法完成状态数据的加载和保存。

2.2.1   Page中的处理

viewstate的作用
图2 Page生命周期

1)     InitRecursive

在Page的生命周期中有3处与ViewState相关,在初始化阶段调用Control. InitRecursive,它递归对所有的控件进行初始化,其中调用了Control.TrackViewState。TrackViewState中打开跟踪ViewState开关。

2)     LoadAllState

在初始化完成之后会调用Page.LoadAllState,LoadAllState只有在PostBack的时候才会执行,它的主要功能是将从页面传递来的__VIEWSTATE的值反序列化为Pair类型的对象,然后将这个对象中存储的ViewState的值加载到Page及所有控件中。实际上LoadAllState加载了ControlState(控件状态)及ViewState(视图状态),本文主要是讨论ViewState,对ControlState部分的处理不进行描述。

LoadAllState中主要有两步:Page.LoadPageStateFromPersistenceMedium和Control.LoadViewStateRecursive。

在LoadPageStateFromPersistenceMedium中发生了如下的调用层次

Page.LoadPageStateFromPersistenceMedium

è    HiddenFieldPageStatePersister.Load

è    ObjectStateFormatter.Deserialize

以上完成的功能是将客户端提交的_VIEWSTATE反序列化为一个类型为Pair的对象pair。

Control.LoadViewStateRecursive中将递归加载控件的ViewState,具体在下面进行讲解。

3)     SaveAllState

SaveAllState它的操作和LoadAllState相反。SaveAllState中主要有两步Control.SaveViewStateRecursive及Page SavePageStateToPersistenceMedium。

Control.SaveViewStateRecursive中将所有控件的ViewState属性递归加载到一个Pair对象中,具体实现细节在下面讲解。

Page SavePageStateToPersistenceMedium中发生如下的调用关系。

Page SavePageStateToPersistenceMedium

è    HiddenFieldPageStatePersister.Save

è    ObjectStateFormatter.Serialize

将Control.SaveViewStateRecursive生成的对象序列化为一个字符串,并赋值给Page.ClientState属性。

在Render阶段发生如下的调用关系:

HtmlForm.RenderChildren

è    Page.BeginFormRender

è    Page.RenderViewStateFields

最终将ClientState属性中的值写入到HTML页面的_VIEWSTATE中。

在Control.InitRecursive中打开跟踪开关,打算对ViewState的值进行跟踪,在Page.LoadAllState中将客户端提交的__VIEWSTATE的值装载到各个控件的ViewState中,在Page.SaveAllState中将发生变化的ViewState序列化为一个字符串,在Render阶段发送回客户端。

4)     ViewState序列化与反序列化

PageStatePersister 是一个抽象类,是表示将ViewState信息序列化及反序列化机制的基类。在Page.LoadPageStateFromPersistenceMedium中示意代码如下:

protected internal virtual object LoadPageStateFromPersistenceMedium()

{

          PageStatePersister pageStatePersister = this.PageStatePersister;

          pageStatePersister.Load();

          return new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);

}

在Page.SavePageStateToPersistenceMedium中的示意代码如下:

protected internal virtual void SavePageStateToPersistenceMedium(object state)

{

        PageStatePersister pageStatePersister = this.PageStatePersister;

        Pair pair = (Pair) state;

        pageStatePersister.ControlState = pair.First;

        pageStatePersister.ViewState = pair.Second;

        pageStatePersister.Save();

}

在Asp.net2.0中实现PageStatePersister这个抽象类,具体提供持久化机制的类是HiddenFieldPageStatePersister。它实现了Load和Save两个方法,Load时将__VIEWSTATE反序列化为一个Pair对象,Save时将Pair对象序列化为一个字符串赋值给Page.ClientState。HiddenFieldPageStatePersister中采用的格式器是ObjectStateFormatter,其实现string Serialize(object state),和object Deserialize(string serializedState)这两个方法,从而实现对Pair对象的序列化和反序列化。

public string Serialize(object state)中的示意代码如下:

MemoryStream memoryStream = GetMemoryStream();

Serialize(memoryStream, state);

byte[] buf = memoryStream.GetBuffer();

if (RequiresViewStateEncryptionInternal)

{

 buf = MachineKeySection.EncryptOrDecryptData(true, buf, this.GetMacKeyModifier(), 0, length);

 length = buf.Length;

}

else if (EnableViewStateMac)

{

buf = MachineKeySection.GetEncodedData(buf, this.GetMacKeyModifier(), 0ref length);

}

return Convert.ToBase64String(buf, 0, length);

将state序列化为内存流,在将其转换为字节流。如果需要加密则对其进行加密处理,否则需要Mac则进行Mac处理。最后将字节流进行Base64编码转换为字符串。

public object Deserialize(string serializedState) 中的示意代码如下:

byte[] buf = Convert.FromBase64String(serializedState);

int length = buf.Length;

if (ContainsEncryptedViewState)

{

buf = MachineKeySection.EncryptOrDecryptData(false, buf, this.GetMacKeyModifier(), 0, length);

 length = buf.Length;

}

else if (EnableViewStateMac)

{

buf = MachineKeySection.GetDecodedData(buf, this.GetMacKeyModifier(), 0, length, ref length);

}

MemoryStream memoryStream = GetMemoryStream();

memoryStream.Write(buf, 0, length);

return this.Deserialize(memoryStream);

将字符串进行Base64解码为字节流,如果需要解密则进行解密处理,否则需要进行需要Mac则进行Mac处理,将字节流转换为内存流,进行反序列化返回Pair对象。


顺便交流一下我的小经验:

1、尽量不要用asp.net自带的验证控件,在批量录入数据时大量的验证控件生成很多页面代码;
2、如果htmlcontrol能满足你的要求,就可以不用webcontrol;
3、很多时候用repeater也很不错,不一定都用gridview;
4、对于ViewState 比较大的页面,可以考虑压缩;
5、对于移动的应用,可以考虑把ViewState 放到session中;
6、用javascript动态创建控件,也能够压缩不少页面代码;
7、利用一些成熟的框架(如jquery),能够大大提高开发效率,提高UI体验,还得靠javascript做很多工作;

8、如果控件的ViewState没有必要,就要设置EnableViewState =false,对于gridview,ViewState有蛮多重要的信息(如分页信息),所以不能gridview.EnableViewState =false,但是可以用gridview.Controls[0].EnableViewState=false,以清除没有用的ViewState。




ViewState用于记录同一个Page的不同请求之间保存和还原服务器控件的视图状态。那么它到底能存储什么信息呢?主要有两部分组成:

            1  程序员通过调用ViewState[""]存储的信息
            2  通过编程改变的控件状态

      ViewState的使用

      Asp.net内置控件的状态值都是通过ViewState存储的,如Textbox的Text属性值、TextMode属性等

[Bindable( true , BindingDirection.TwoWay), PersistenceMode(PersistenceMode.EncodedInnerDefaultProperty), Editor( " System.ComponentModel.Design.MultilineStringEditor,System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a " typeof (UITypeEditor)), Localizable( true ), WebCategory( " Appearance " ), DefaultValue( "" ), WebSysDescription( " TextBox_Text " )]
public   virtual   string  Text
{
    
get
    
{
        
string str = (stringthis.ViewState["Text"];
        
if (str != null)
        
{
            
return str;
        }

        
return string.Empty;
    }

    
set
    
{
        
this.ViewState["Text"= value;
    }

}

 
[Themeable(
false ), WebSysDescription( " TextBox_TextMode " ), DefaultValue( 0 ), WebCategory( " Behavior " )]
public   virtual  TextBoxMode TextMode
{
    
get
    
{
        
object obj2 = this.ViewState["Mode"];
        
if (obj2 != null)
        
{
            
return (TextBoxMode) obj2;
        }

        
return TextBoxMode.SingleLine;
    }

    
set
    
{
        
if ((value < TextBoxMode.SingleLine) || (value > TextBoxMode.Password))
        
{
            
throw new ArgumentOutOfRangeException("value");
        }

        
this.ViewState["Mode"= value;
    }

}

      也可以使用ViewState存储自己想要存储的信息,如ViewState["no"] =this.no

      注:ViewState记录的信息只用于同一个页面的请求间使用,不同页面之间不起作用

      ViewState作用时间

      在Page的生命周期中,并不是任何时候都可以访问ViewState。必须在InitComplete事件开始才能访问,在PreInit及Init事件内不起作用。可以显示的调用TraceViewState()函数来启动ViewState。       

protected   override   void OnInit(EventArgs e)
(
    
bool ret;
    ret 
=  ViewState.IsItemDirty( " item " );     // returns false
    ViewSTate[
" item " =   " 1 ";
    ret 
=  ViewState.IsItemDirty( " item " );     // returns false

    
base .OnInit(e);
}

      在Init方法里,即使给ViewState赋值,也无法使用。

     protected   void  Page_InitComplete( object sender, EventArgs e)
    
{
        
bool ret;
        ret 
= ViewState.IsItemDirty("item");   //returns false
        ViewState[
"item"= "1";
        ret 
= ViewState.IsItemDirty("item");  //returns true
    }

      在InitComplete事件里,由于调用了TrackViewState(),在ViewState内保存的项就会被保存(项被标记为Dirty)

      ViewState 和 PostBack

      控件的ViewState功能可以开启也可以关闭,如果关闭,是否在post过程中就不会保存任何信息呢。下面是test code

< script runat = " server " >
    
void  btnSubmit_Click(Object sender, EventArgs e)
    
{
        lblMessage.Text 
= "Goodbye everyone";
        lblMessage1.Text 
= "Goodbye everyone";
        txtMessage.Text 
= "Goodbye everyone";
        txtMessage1.Text 
= "Goodbye everyone";
    }

</ script >
< form id = " form1 "  runat = " server " >
  
   
< asp:Label runat = " server "  ID = " lblMessage "  EnableViewState  = true   
   Text
= " Hello World " ></ asp:Label >
   
< asp:Label runat = " server "  ID = " lblMessage1 "  EnableViewState  = false   
   Text
= " Hello World " ></ asp:Label >
   
< asp:Textbox runat = " server "  ID = " txtMessage "  EnableViewState  = true   
   Text
= " Hello World " ></ asp:Textbox >
   
< asp:Textbox runat = " server "  ID = " txtMessage1 "  EnableViewState  = false   
   Text
= " Hello World " ></ asp:Textbox >

< br  />
   
< asp:Button runat = " server "  Text = " Change Message "  ID = " btnSubmit " ></ asp:Button >
< br  />
   
< asp:Button ID = " btnEmptyPostBack "  runat = " server "  Text = " Empty Postback " ></ asp:Button >
</ form >

      由两个Label、两个Textbox、两个Button组成。点击“Change Message”,执行btnSubmit_click事件,将所有控件的值被置为“Goodbye everyone”,这与预期结果一样。然后,点击“Empty PostBack”引发空回送,EnableViewState=false的控件(一个Label、一个Textbox)是否不保存前一个状态(Goodbye everyone)呢。结果是Label的值恢复为Hello World,而Textbox的值保留了前一个值“Goodbye everyone”。

      其原因是在于asp.net 2.0新增的事件LoadPostbackData,在此事件中,实现了IPostBackEventHandler的控件会使用回送数据重置控件的状态。有以下三个重要点:
    1 此阶段的数据不使用ViewState中获取的,而是从回送数据表中获取的
    2 只把回送数据分发给实现了IPostBackEventHandler的控件,如Textbox、Checkbox
    3 由于此事件发生在LoadViewState事件之后,此时会重写控件来自ViewState的值

    TextBox的LoadPostBackData()方法定义如下:

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;
}

    其中postDataKey为控件的主要标识符,postCollection为发送到服务器的名/值对的集合。首先进行验证操作,然后根据控件的标识从键值对集合中取出回送数据,如果允许更改,则更改Text属性值,这样从用户传回来的值就被重新设置到Textbox控件上,用户看到的还是提交前的数据。

    子控件的ViewState

     每个控件一般会经历如下几个阶段: 

      1。Instantiate 
      2。Initialize 
      3。Begin Tracking View State 
      4。Load View State (postback only) 
      5。Load Postback Data (postback only) 
      6。Load 
      7。Raise Changed Events (postback only, optional)
      8。Raise Postback Events (postback only, optional)
      9。PreRender
      10。SaveViewState
      11。Render
      12。Unload
      13。Dispose

    对于Page对象,是在InitComplete()事件中开启ViewState的,而Page内的control则是在Init后开启的。test code如下;

< html xmlns = " http://www.w3.org/1999/xhtml " >
< head runat = " server " >
    
< title > Untitled Page </ title >
</ head >
< body >
    
< form id = " form1 "  runat = " server " >
    
< div >
        
< asp:Label ID = " Label1 "  runat = " server "  Text = " StaticText " ></ asp:Label >
        
< asp:Button ID = " Button1 "  runat = " server "  Text = " PostBack "  
            onclick
= " Button1_Click "   />
    
</ div >
    
</ form >
</ body >
</ html >

/********************************************************************/
   
    
protected   void  Page_PreInit( object  sender, EventArgs e)
    
{
        
if (!IsPostBack)
        
{
            Label1.Text 
= "dymaticText";
        }

    }


    
protected   void  Page_Init( object  sender, EventArgs e)
    
{
        
if (!IsPostBack)
        
{
            Label1.Text 
= "dymaticText";
        }

    }


    
protected   void  Page_Load( object  sender, EventArgs e)
    
{
        
if (!IsPostBack)
        
{
            Label1.Text 
= "dymaticText";
        }

    }


   
protected   void  Button1_Click( object  sender, EventArgs e)
    
{

    }

    在PreInit、Init、及Load事件中,修改Label1的Text属性值。下面将测试这三个事件,哪些会使ViewState保存通过program修改的控件值。   

    注释掉PreInt、Init中代码,保留Load事件,由于在Load事件在InitComplete事件之后,所以毫无疑问,ViewState会保存对Label1控件的修改,将"symaticText"值保存在"__VIEWSTATE"隐藏字段中,当按下Button按钮回送页面时,在LoadViewState阶段,会从ViewState中提取dymaticText,并将值赋值给Label1控件的Text属性。最终显示在页面的是"dymaticText"字符串。

    注释掉Init、Load,保留PreInit事件,由于PreInit事件在InitComplete事件之后,所以在此阶段更改的数据不会保存,回送后最终显示在页面的是设计时赋的值"StaticText".

      注释掉PreInit、Load,保留Init事件。Init事件在InitComplete事件之前,但是回送后显示在页面的也是“dymaticText”。其原因如下:
      由Page Life Cycle 之一: Overview一节知道,InitComplete不是递归事件,它只能有Page对象启动,Control控件不包含此事件。Control是在Init事件中开始track ViewState的,而Page的Init是递归事件,在此阶段page对象会调用子控件的Init事件处理函数, 所以从Page.Init开始,就可以保存Page的子控件的视图状态了。这就解释了测试代码的结果。

      动态子控件 和 ViewState

      可以在运行时根据需要动态的加载控件到Page对象,如下面代码那样: 

  Label lbl  =   new  Label();
  Page.Controls.Add(lbl);

     因为动态控件是在运行时加的,它不包含在编译好的类中,所以控件树不包含动态控件。必须在Page的每一次加载过程中运行上面的代码,以示它能插入到控件树中,并最终显示到页面。对动态控件的初始化操作或添加操作都不能包含在!IsPostBack条件语句中。

    此外,动态控件一旦被添加到控件组,就会执行生命周期的“追赶”过程。也就是说,它会把它错过的事件依次执行一遍。比如,如果add操作是在Page_Load事件中,当添加到Controls中后,它会依次执行Init、LoadVewState、LoadPostBackData,直至追赶到Page目前执行的Load事件。原则上,用户可以在Render之前的任意阶段添加动态控件,但一般推荐在PreInit、Init阶段,因为最好在启用ViewState的InitComplete之前把控件添加到控件树上,以保存其视图状态。

< html >
< script language = " C# "  runat = " server " >
    
void  Page_Load(Object sender, EventArgs e)
    
{
       DropDownList ddlDynamic 
= new DropDownList();
       ddlDynamic.ID 
= "ddlDynamic";

       form1.Controls.Add(ddlDynamic);       
//(1)
        
       
if (!IsPostBack)
       
{
            
for (int i=1; i <=3; i++)
                ddlDynamic.Items.Add(
new ListItem(i.ToString(), i.ToString()));
       }


       
//form1.Controls.Add(ddlDynamic);        //(2)     
     
       
if (IsPostBack)
       
{
            Response.Write(
"[Page_Load]静态:" + ddlStatic.SelectedIndex + "<BR>");
            Response.Write(
"[Page_Load]动态:" + ddlDynamic.SelectedIndex + "<BR>");
       }

    }

    
void  Button_Click(Object sender, EventArgs e)
    
{
        DropDownList ddlDynamic 
= (DropDownList)form1.FindControl("ddlDynamic");
        Response.Write(
"[Button_Click]静态:" + ddlStatic.SelectedIndex + "<BR>");
        Response.Write(
"[Button_Click]动态:" + ddlDynamic.SelectedIndex + "<BR>");
    }

   
</ script >
< body >
 
< form id = " form1 "  runat = " server " >
< asp:Button id = " btn "  runat = " server "  Text = " Click Me "  OnClick = " Button_Click "   />   
  
< br />
  静态: 
< asp:DropDownList id = " ddlStatic "  runat = " server " >
          
< asp:ListItem Text = " 1 "  Value = " 1 "   />
          
< asp:ListItem Text = " 2 "  Value = " 2 "   />
          
< asp:ListItem Text = " 3 "  Value = " 3 "   />
        
</ asp:DropDownList >  
  
< br />
  动态:
 
</ form >
</ body >
</ html >

    上面的测试代码中,form1.Controls.Add操作放在(1)、(2)两个不同的地方,执行结果却大不一样。

    代码(1):执行结果与预期相同。首次执行时,动态下拉框填充了数据,分别选择第二项和第三项,然后点击按钮回送,页面上显示:

[Page_Load]静态: 1
[Page_Load]动态:
0
[Button_Click]静态:
1
[Button_Click]动态:
2

    并且两个下拉框中保存了上次选择的项。

    代码(2):首次执行时,动态下拉框填充数据。分别选择第二项和第三项,然后点击按钮回送,页面显示:

[Page_Load]静态: 1
[Page_Load]动态:
- 1
[Button_Click]静态:
1
[Button_Click]动态:
- 1

     没有保存动态下拉框的选项,并且动态下拉框中没有数据。

     根据前面的原理我们可以分析出,其原因如下:
    1  填充下拉框的代码包含在 !IsPostBack代码内,回送时自然不会填充控件;
    2  执行fomr1.Controls.Add(ddlDynamic)代码时,DropDownList实例对象会依次执行Init、LoadViewState、LoadPostbackData。把这句话放在填充数据的前面,对control所作的更改才能保存在ViewState中。而如果放在后面,只是把控件放入控件树,并没有保存对它的更改记录,在回送时,ViewState中没有它的记录。如果把 !IsPostBack去掉,就能够填充控件了。
    3  去掉 !IsPostBack,虽然可以填充数据,但执行结果如下:

 

[Page_Load]静态: 1
[Page_Load]动态:
0
[Button_Click]静态:
1
[Button_Click]动态:
2

    在PageLoad阶段没能设置PostBack值,但在Button_Click阶段却正确设置了。其原因是什么呢?

     Page.ProcessRequestMain函数定义如下(经删减后的部分),从它的定义中我们可以看到一个页面所经历的全过程。 

private   void  ProcessRequestMain( bool  includeStagesBeforeAsyncPoint,  bool  includeStagesAfterAsyncPoint)
{
    HttpContext context 
= this.Context;

    
if (this.PageAdapter != null)
    
{
        
this._requestValueCollection = this.PageAdapter.DeterminePostBackMode();
    }

    
else
    
{
        
this._requestValueCollection = this.DeterminePostBackMode();
    }


    
this.PerformPreInit();
    
this.InitRecursive(null);
    
this.OnInitComplete(EventArgs.Empty);
    
this.LoadAllState();
    
this.ProcessPostData(this._requestValueCollection, true);
    
this.OnPreLoad(EventArgs.Empty);
    
this.LoadRecursive();
    
this.ProcessPostData(this._leftoverPostData, false);
    
this.RaiseChangedEvents();
    
this.RaisePostBackEvent(this._requestValueCollection);
    
this.OnLoadComplete(EventArgs.Empty);
    
this.PreRenderRecursiveInternal();
    
this.PerformPreRenderComplete();
    
this.SaveAllState();
    
this.OnSaveStateComplete(EventArgs.Empty);
    
this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
}

    从定义看出,Page在Load和LoadComplete之间还包含三个阶段:
    ProcessPostData second try:在Load之后,处理未处理过的PostBack data。这种方式允许用户在Load方法内加载动态控件,并获取用户的输入数据。
    RaiseChangedEvents:服务器控件通知asp.net状态发生了更改
    RaisePostBackEvents:进行了加载并发出了更改通知后,在预呈现发生前触发此事件。通知引起回发的服务器控件,使其处理传入的回发事件。如Button控件的Click事件,从而进入Click事件处理程序

    根据上面的分析,可以知道最后一个问题的原因了:在Load前的ProcessPostBackData阶段,由于还未对动态DropDownList填充数据,无法给它赋予post data(这里是SelectedIndex),所以在Load里无法获取用户所选的项。但在Load之后,数据已填充完成后,就可以在第二个ProcessPostBackData中设置post data了,Button_Click是在第二次ProcessPostBackData之后的RaisePostBackEvent中触发的,所以在Click事件里就能够正确获取用户所选项了

    接下来仔细查看两个方法的定义:

 

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();
                }

            }

        }

    }

}

 
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();
        }

    }

}


[EditorBrowsable(EditorBrowsableState.Advanced)]
protected   virtual   void  RaisePostBackEvent(IPostBackEventHandler sourceControl,  string  eventArgument)
{
    sourceControl.RaisePostBackEvent(eventArgument);
}

   RaiseChangeedEvent事件中每一个状态发生了更改的控件(实现了IPostBackDataHandler接口)都执行RaisePostBackChangedEvent,通知asp.net该控件状态执行了更改。 在此例中是DropDownList空间内容发生更改,DropDownList控件的实现内容如下: 

protected   virtual   void  RaisePostDataChangedEvent()
{
    
if (this.AutoPostBack && !this.Page.IsPostBackEventControlRegistered)
    
{
        
this.Page.AutoPostBackControl = this;
        
if (this.CausesValidation)
        
{
            
this.Page.Validate(this.ValidationGroup);
        }

    }

    
this.OnSelectedIndexChanged(EventArgs.Empty);
}

    该事件的调用引发DropDownList控件的SelectdIndecChanged事件,如果用户定义了该事件的处理程序,则进入处理程序继续执行。

    RaisePostBackEvent则是调用引起回送的控件(实现了IPostBackEventHandler接口)的RaisePostBackEvent事件,从而进入回送事件处理程序。Button控件的RaisePostBackEvent实现如下:

protected   virtual   void  RaisePostBackEvent( string  eventArgument)
{
    
base.ValidateEvent(this.UniqueID, eventArgument);
    
if (this.CausesValidation)
    
{
        
this.Page.Validate(this.ValidationGroup);
    }

    
this.OnClick(EventArgs.Empty);
    
this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
}

    通过触发RaisePostBackEvent进入Button的Click事件处理程序。

 

参考:
    http://www.codeproject.com/KB/aspnet/ASPViewStateandPostBack.aspx
    http://www.codeproject.com/KB/aspnet/aspnetviewstatepagecycle.aspx
    http://blog.joycode.com/saucer/archive/2004/10.aspx  


你可能感兴趣的:(view)