为非回调页面保存状态

来源:互联网 发布:域名由哪几部分组成 编辑:程序博客网 时间:2024/05/23 23:50

做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,两个主要问题都解决了,接下来就可以用代码来实现。

  1.         Pair _state;
  2.         /// <summary>
  3.         /// 已重载。如果可能,从服务端内存中还原视图内容。
  4.         /// </summary>
  5.         /// <returns></returns>
  6.         protected override object LoadPageStateFromPersistenceMedium()
  7.         {
  8.             object state;
  9.             if (_state != null)
  10.                 state = _state.First;
  11.             else
  12.                 state = base.LoadPageStateFromPersistenceMedium();
  13.             return state;
  14.         }
  15.         /// <summary>
  16.         /// 已重载。将当前视图内容和请求参数集合保存至服务器内存。
  17.         /// </summary>
  18.         /// <param name="state"></param>
  19.         protected override void SavePageStateToPersistenceMedium(object state)
  20.         {
  21.             base.SavePageStateToPersistenceMedium(state);
  22.             var ps = _state;
  23.             if (ps == null)
  24.             {
  25.                 ps = new Pair(state, base.DeterminePostBackMode());
  26.                 Session["page_state"] = ps;
  27.             }
  28.             else
  29.                 ps.First = state;
  30.         }
  31.         /// <summary>
  32.         /// 已重载。当基类方法返回空值时,尝试从服务端内存中获取先前保存的请求参数集合。
  33.         /// </summary>
  34.         /// <returns></returns>
  35.         protected override System.Collections.Specialized.NameValueCollection DeterminePostBackMode()
  36.         {
  37.             var coll = base.DeterminePostBackMode();
  38.             if (coll == null)
  39.             {
  40.                 _state = Session["page_state"as Pair;
  41.                 if (_state != null)
  42.                     coll = _state.Second as System.Collections.Specialized.NameValueCollection;
  43.             }
  44.             return coll;
  45.         }

以上代码的实现方法仅提供一种思路,并不是最终解决办法,因为还有许多细节的问题要考虑,并且也没有进行过广泛的应用,因此对可能造成的问题基本上是不可知的。欢迎有兴趣的人一起讨论。

原创粉丝点击