为非回调页面保存状态

    [本文为摘抄,原作者:风谷]

    做ASP.NET开发的人都知道,控件使用ViewState保存自身的状态,并通过回调时的表单值来控制自身的行为,例如显示数据、触发事件等。在默认情况下,ViewState是通过发送到浏览器页面中的一个隐藏域(HiddenField)来进行值的传递,因此,只有在页面回调的时候才能取到ViewState中的相应值。当然,这也是ASP.NET的基本处理方式。


  但在某些应用上,这种处理方式可能会无法满足我们的需求。举例来说,一个典型查询页,上面会提供一些查询条件让用户选择,用户在输入查询条件之后找到自己想要的结果,然后点击相应的结果进入另一个页面(姑且称之为内容页吧),在这个页面中完成自己操作后,返回到查询页。一般情况下,我们所提供的返回功能往往是只一个到查询页的链接,那么进入重新进入这个页面时,之前所输入的查询条件都将会丢失,自然查询结果也就不会存在了。这时,如果用户需要先前的查询条件和结果,甚至是页码,就必须重复先前的查询操作去获取结果。

  上述这种情况对于一些简单的查询来说并不是很大的问题,但对于一些数据量大、查询条件复杂的情况会非常麻烦,用户不得不多次重复做一些相同的事,给使用带来了级大的不便。要如何优化这种情况呢?一些人可能会说,返回功能不要用链接,而是用脚本在客户端调用history.back()让浏览器后退一步就可以了。不错,这是一种解决办法,但是,如果在内容页里进行了一些回调,这种简单的“后退”仍旧是不合适的。那么,要怎么样做才能满足要求呢?

  先来分析一下问题,控件的状态是通过ViewState保存的,因此要还原先前用户输入的内容了,就必须在页面处理请求时加载ViewState,另外,为了完整地还原页面的状态,除了ViewState以外,还需要保存从客户端POST过来的所有表单内容。但如本文开头所说,ViewState是保存中浏览器的隐藏域里,如果不是回调就没办法取到,另外,ASP.NET也不会在非回调的情况下加载ViewState。很明显,如果要在非回调的情况下还原控件状态,有两个问题需要解决,一是要在非回调的情况下获取最后一次请求页面时ViewState和表单的内容,二是要让Page认为当前请求为回调,即Page.IsPostBack属性值为true,因为只有这样才能完整地模拟PostBack操作。

  要解决第一个问题并不复杂,我们可以把ViewState和表单的内容保存在服务端内存中,可以重载Page.SavePageStateToPersistenceMedium方法将值存入服务端内存。但重载该方法并不能直接获取表单内容,但可以通过Page.DeterminePostBackMode方法取得。这样,只要把两个变量以合适的方法放入内存就可以了。我选择的办法是用一个Pair对象保存视图和表单内容,再将它们放入Session中。当然,除了Session,还可以用HttpRuntime.Cache等。如果要加载保存在服务端内存中的内容,可以重载Page.LoadPageStateToPersistenceMedium方法。

  第二个问题就是要让Page认为当前是回调模式,即IsPostBack返回的值应为true。通过对System.Web源代码的分析,我发现当Page.DeterminePostBackMode方法返回的值不是空值时,就可以达到我们的目的,而这个方法又是可以重载的,这样问题就变得简单了。重载DeterminePostBackMode方法,若基类方法返回的值是空值,就从先前在Session中保存的状态取得上一次保存的表单值。其实关于DeterminePostBackMode方法,MSDN中的解释并不多,我也是从字面上猜测,结果对源码的分析得来的,是否合理还有待验证。

  OK,两个主要问题都解决了,接下来就可以用代码来实现。

Pair _state; 

/// <summary> 
/// 已重载。如果可能,从服务端内存中还原视图内容。 
/// </summary> 
/// <returns></returns> 
protected override object LoadPageStateFromPersistenceMedium() 

object state; 

if (_state != null) 
state = _state.First; 
else 
state = base.LoadPageStateFromPersistenceMedium(); 

return state; 


/// <summary> 
/// 已重载。将当前视图内容和请求参数集合保存至服务器内存。 
/// </summary> 
/// <param name="state"></param> 
protected override void SavePageStateToPersistenceMedium(object state) 

base.SavePageStateToPersistenceMedium(state); 

var ps = _state; 

if (ps == null) 

ps = new Pair(state, base.DeterminePostBackMode()); 
Session["page_state"] = ps; 

else 
ps.First = state; 



/// <summary> 
/// 已重载。当基类方法返回空值时,尝试从服务端内存中获取先前保存的请求参数集合。 
/// </summary> 
/// <returns></returns> 
protected override System.Collections.Specialized.NameValueCollection DeterminePostBackMode() 

var coll = base.DeterminePostBackMode(); 

if (coll == null) 

_state = Session["page_state"] as Pair; 
if (_state != null) 
coll = _state.Second as System.Collections.Specialized.NameValueCollection; 


return coll; 


  以上代码的实现方法仅提供一种思路,并不是最终解决办法,因为还有许多细节的问题要考虑,并且也没有进行过广泛的应用,因此对可能造成的问题基本上是不可知的。欢迎有兴趣的人一起讨论。
posted @ 2009-02-05 11:27  普若伽门  阅读(483)  评论(0编辑  收藏  举报