页面的postback

     点击页面的控件会导致页面的postback,那么postback后页面是如何处理并且最终引发服务器端的事件的呢?下面我们就来简单的结合源代码叙述一下。

 

     

ContractedBlock.gif ExpandedBlockStart.gif Code
  /// <summary>
        
/// 
        
/// </summary>
        
/// <param name="postData">在load之前的那次调用是使用的request.Form里面的集合,
        
/// 在load之后的那次调用是使用的load之前控件还没有生成的键值对集合,而且在第一次已经被添加进入_leftoverPostData</param>
        
/// <param name="fBeforeLoad">true:load前的调用;false:load后的调用</param>
        private void ProcessPostData(NameValueCollection postData, bool fBeforeLoad)
        {
            
//_changedPostDataConsumers 执行了控件的 LoadPostData 方法后并且返回 true 后被加入到这个集合中,从而在 RaiseChangedEvents 方法中调用
            
//IPostBackDataHandler.RaisePostDataChangedEvent(); 接口方法
            
//如:TextBox,CheckBox 或者 DropDownList 之类的 AutoPostBack 属性:
            
//如果 AutoPostBack 为 true,则在向客户端输出时,加入上面的 __doPostBack 式的回发;如为 false,则不加入这样的代码立即回发脚本,
            
//而是等待有其他可以引起回发的控件(比如 Button, LinkButton 等)回发后,
            
//在   this.RaiseChangedEvents(); 中触发 TextBox 的 TextChanged 事件, CheckBox 的 CheckedChanged 事件,DropDownList 的 SelectedIndexChanged 事件;在 RaisePostBackEvent 时继续分析其他引起回发的事件。
            if (this._changedPostDataConsumers == null)
            {
                
this._changedPostDataConsumers = new ArrayList();
            }
            
if (postData != null)
            {
                
//回传数据集合
                foreach (string str in postData)
                {
                    
//没有值或者是系统字段(如:  "__EVENTTARGET","__EVENTARGUMENT","__VIEWSTATEFIELDCOUNT","__VIEWSTATE","__VIEWSTATEENCRYPTED","__PREVIOUSPAGE","__CALLBACKID","__CALLBACKPARAM","__LASTFOCUS" 等等)
                    if ((str == null|| IsSystemPostField(str))
                    {
                        
continue;
                    }
                    Control control 
= this.FindControl(str);
                    
if (control == null)
                    {
                        
//没有在页面上找到这个控件,并且在load前,说明是动态添加的控件,这个时候控件还没有生成,把key添加进去_leftoverPostData
                        
//在load之后的调用再进行处理
                        if (fBeforeLoad)
                        {
                            
if (this._leftoverPostData == null)
                            {
                                
this._leftoverPostData = new NameValueCollection();
                            }
                            
this._leftoverPostData.Add(str, null);
                        }
                        
continue;
                    }
                    IPostBackDataHandler postBackDataHandler 
= control.PostBackDataHandler;
                    
if (postBackDataHandler == null)
                    {
                        
if (control.PostBackEventHandler != null)
                        {
                            
//控件存在,没有实现IPostBackDataHandler,但是实现了IPostBackEventHandler,例如Button 我们为它注册事件。
                            
//注意:这里只有被点击的 Button 会在 Request.Form 集合中生成一个 name-value 对,其他的不会
                            this.RegisterRequiresRaiseEvent(control.PostBackEventHandler);
                        }
                    }
                    
else
                    {
                        
if ((postBackDataHandler != null&& postBackDataHandler.LoadPostData(str, this._requestValueCollection))
                        {
                            
//需要调用 IPostBackDataHandler.RaisePostDataChangedEvent()
                            this._changedPostDataConsumers.Add(control);
                        }
                        
//从._controlsRequiringPostBack(LoadPageViewState时对它进行了赋值)中移除该控件. 
                        
//这样就从_controlsRequiringPostBack中移除了所有实现IPostBackDataHandler接口的控件. 
                        
//接着继续对 _controlsRequiringPostBack中余下的控件进行处理,
                        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 handler2 
= control2._adapter as IPostBackDataHandler;
                        
if (handler2 == null)
                        {
                            handler2 
= control2 as IPostBackDataHandler;
                        }
                        
if (handler2 == null)
                        {
                            
throw new HttpException(SR.GetString("Postback_ctrl_not_found"new object[] { str2 }));
                        }
                        
if (handler2.LoadPostData(str2, this._requestValueCollection))
                        {
                            
this._changedPostDataConsumers.Add(control2);
                        }
                        
continue;
                    }
                    
if (fBeforeLoad)
                    {
                        
if (list == null)
                        {
                            list 
= new ArrayList();
                        }
                        list.Add(str2);
                    }
                }
                
this._controlsRequiringPostBack = list;
            }
        }



        
/// <summary>
        
/// 这里只有被点击的 Button 会在 Request.Form 集合中生成一个 name-value 对,而且引发页面回传的 控件也只应该有一个,所以使用的“=”
        
/// </summary>
        
/// <param name="control"></param>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        
public virtual void RegisterRequiresRaiseEvent(IPostBackEventHandler control)
        {
            
this._registeredControlThatRequireRaiseEvent = control;
        }



        
/// <summary>
        
/// 如果上面的 _registeredControlThatRequireRaiseEvent 被赋值了,证明通过 RegisterRequiresRaiseEvent方法注册的事件
        
/// 因为 Button 会自动回发
        
/// </summary>
        
/// <param name="postData"></param>
        private void RaisePostBackEvent(NameValueCollection postData)
        {
            
if (this._registeredControlThatRequireRaiseEvent != null)
            {
                
this.RaisePostBackEvent(this._registeredControlThatRequireRaiseEvent, null);
            }
            
else
            {
                
//如 LinkButton (对应于 HTML 的 A 元素),DropDownList (对应于 HTML 的 SELECT)等,则并不会自动引起回发。这种情况下,
                
//这时候 会自动生成一些脚本 如: href="javascript:__doPostBack('LinkButton1','')" 而__doPostBack('LinkButton1','')
                
//方法__EVENTTARGET,__EVENTARGUMENT分别赋值,并且值被传递回服务器端
                /*
                 *  <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
                    <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />    
                 * <script type="text/javascript">
                    <!--
                    var theForm = document.forms['Form2'];
                    if (!theForm) {
                        theForm = document.Form2;
                    }
                    function __doPostBack(eventTarget, eventArgument) {
                        if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
                            theForm.__EVENTTARGET.value = eventTarget;
                            theForm.__EVENTARGUMENT.value = eventArgument;
                            theForm.submit();
                        }
                    }
                    // -->
                    </script>
                 
*/


                
//下面代码通过判断回传的值 __EVENTTARGET 证明是不是通过 __doPostBack 回传注册事件的。
                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"];
                        
//调用控件实现的 IPostBackEventHandler.RaisePostBackEvent 这个接口方法s
                        this.RaisePostBackEvent(control.PostBackEventHandler, eventArgument);
                    }
                }
                
else
                {
                    
this.Validate();
                }
            }
        }

        
/// <summary>
        
/// 调用控件实现的 IPostBackEventHandler.RaisePostBackEvent 这个接口方法
        
/// </summary>
        
/// <param name="sourceControl"></param>
        
/// <param name="eventArgument"></param>
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        
protected virtual void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
        {
            sourceControl.RaisePostBackEvent(eventArgument);
        }

 

 

 

 

1实现的 IPostBackEventHandler 接口来注册回传事件,并且需要在控件里面传递参数的。
eg:
table1.Attributes.Add("OnClick", this.Page.GetPostBackClientEvent(this, pMenuItem.ID));

在生成html页面 的注册形式变为:

OnClick="__doPostBack('ToolBar1','tbSave')"
OnClick="__doPostBack('ToolBar1','tbCancel')"

//实现   IPostBackEventHandler 接口的方法
void IPostBackEventHandler.RaisePostBackEvent(string itemID)
{
  this.OnMenuItemClick(new MenuItemClickEventArgs(this.SetMenuItemByitemID(itemID)));
}
其实这里也可以在OnPreRender中使用   this.Page.RegisterRequiresRaiseEvent(this);来强制调用 RaisePostBackEvent

2实现 IPostBackDataHandler 接口来注册回传事件,这里不需要传递参数。

因为通过调用方法 protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
postCollection[postDataKey]可以取到回传的数据进行处理,所以这里就需要控件存在一个 name = UniqueID 的input控件
private HtmlInputHidden SelectedTab;
this.SelectedTab = new HtmlInputHidden();
this.SelectedTab.ID = this.UniqueID;
如果没有这个 input控件,那么不会触发 LoadPostData方法了。
因为这个input控件 的 name和值会被加到 PostData键值对集合中去,而我们调用控件的 LoadPostData是根据PostData 里面的键数据找
到控件,然后调用的控件的LoadPostData 方法的。
其实这里也可以在OnPreRender中使用   this.Page.RegisterRequiresPostBack(this);来强制调用 LoadPostData

[EditorBrowsable(EditorBrowsableState.Advanced)]
public void RegisterRequiresPostBack(Control control)
{
    if (!(control is IPostBackDataHandler) && !(control._adapter is IPostBackDataHandler))
    {
        throw new HttpException(SR.GetString("Ctrl_not_data_handler"));
    }
    if (this._registeredControlsThatRequirePostBack == null)
    {
        this._registeredControlsThatRequirePostBack = new ArrayList();
    }
    this._registeredControlsThatRequirePostBack.Add(control.UniqueID);
}
看上面,这里是 Add 说明可以调用多个控件的 LoadPostData 方法的。这里
this._registeredControlsThatRequirePostBack 和 _registeredControlThatRequireRaiseEvent
是有区别的

 

在生成html页面 的注册形式变为:
onClick="Tab_OnSelectServerClick(this,'TabControl1:tabPage1');__doPostBack('TabControl1','')"

//实现   IPostBackDataHandler 接口的LoadPostData方法
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;
}
接着随着我们LoadPostData 的返回值来决定是否调用RaisePostDataChangedEvent 方法
protected virtual void RaisePostDataChangedEvent()
{

}

 

 

总结一下:
我们写控件的时候可以继承 IPostBackDataHandler IPostBackEventHandler
两个接口来来处理数据和回发页面
当我们的控件只继承IPostBackDataHandler 接口的话,我们在控件中必须满足条件:
控件里面存在一个 ID 为 UniqueID的input控件,或者在OnPreRender中使用   this.Page.RegisterRequiresPostBack(this);
只有这样 IPostBackDataHandler 中的 LoadPostData接口方法才会被调用。

而IPostBackDataHandler 接口中IPostBackDataHandler.RaisePostDataChangedEvent()方法是否被调用
是依靠LoadPostData 返回true 值来决定
如果LoadPostData 返回true

在这个控件里面触发事件的控件是HtmlInputButton,TextBox,CheckBox 或者 DropDownList 之类 那么它的 AutoPostBack 属性被设置成 true立刻回发,触发事件的控件是table,hr 等html元素,我们也可以依靠__dopostback()脚本 让页面立刻回发。
都不满足的话,而是等待有其他可以引起回发的控件(比如 Button, LinkButton 等)回发后,
只要一回发 方法RaisePostDataChangedEvent()必定执行

当我们控件只 继承 IPostBackEventHandler接口的时候 我们可以通过button,__dopostback()脚本(this.Page.GetPostBackClientEvent获得)回发页面并传递参数。

 

 

 

 

转载于:https://www.cnblogs.com/kasafuma/archive/2008/08/14/1267927.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值