页面无限跳转间如何保存页面状态(1)

引子

这是一个极其困难的题目,仅仅是描述一遍,都会非常困难,不过我尝试一下,希望能描述清楚:

我们公司是采用 list/Detail 的页面逻辑,即 list 页面有一个 DataGrid, 列出了一些项,点击其中一项后,页面跳转到 Detail 页面查看该项的详细。 Detail 页面有一个返回按钮,点击后即返回到 list 页面。 list 中的数据列表通常是经过筛选的,如 xxx 大于 20 的,然后还经过了排序的。

现在问题是这样的:

用户要求,当从 Detail 返回到 list 中时,数据列表中的状态不变。

公司认为,这是一个合理的要求,因为数据量实在太大,谁都不想返回 list 就又得从第一页看起。

还要求,刚才看的 Detail 的项目必须是在 list 的数据列表的当前页,而不一定是进入前 list 数据列表的那一页;但如果在 Detail 中把当前项删除,返回则必须是在进入前 list 数据列表的那一页。

更麻烦的是,在 Detail 页时用户可能跳转到若干个相关页,这些相关页也可能还有 DataDrid, 然后用户会在跳入相应的 Detail 页中,这些相应的状态也要得到保持。

而且还会可能出现一个 list 页中会有多个 DataGrid 。

举个例子吧:

用户查看 A_list , A_list 中有 DataGrid_A1 和 DataGrid_A2, 其中的数据都是经过筛选了的;然后他查看项 A1.1 这条数据,这就进入了 A1.1_Detail ;接着又跳入了 A1.1 的相关页面 A1.1_Detail2, 这个页面有一个 DataGrid_A1.1 ,他进行筛选、排序;再跳入 A1.1 的另一个相关页面,这个页面是一个 list 页面 A1.1_List, 其中有一个 DataGrid_A1.11 ,很显然,这个页面的数据也是筛选过的,用户给他排序;再进入其中一项的 Detail 页面 A 1.11.1 ,修改其中的数据……

好,做过这些之后,他返回。先回到了 A1.1_List ,要求 DataGrid_A1.11 的筛选条件、排序方式不变,刚才访问的那一项在 DataGrid 当前页上,之前可能该项时在第二页上,现在可能是在第四页,那就显示第四页吧;接着返回到 A1.1_Detail2 ,这个页面上的 DataGrid_A1.1 筛选条件、排序方式、当前页码不变;再返回到 A1.1_Detail ,删除了这条 A1.1 数据;最后返回到 A_list ,这时要求该页上的两个 DataGrid 的筛选条件、排序方式不变,都显示之前所在的页码。

更可怕的是,真正的用户他未必就会这么原路返回,所以你别想让这些页面状态数据遵循后进先出原则。

请问该怎么做?

出现这个问题的原因

如果是在 Windows 程序中,这就非常简单了,因为从第一,某记录的 Detail 及其相关信息,通常是一个窗体的多个选项卡而已;即使是打开另一个窗体,也不过是把本身 Hide (隐藏)起来了而已,等返回的时候再 Show 出来即可。归根结底, Windows 程序是一个有状态的应用程序,一切都很简单。

到了 Web 就不一样的, Web 是无状态的,页面的每一次回传,都不知道自己的某一个变量之前是什么值,更别说页面之间了,每一个网页,都不知道自己从哪里(哪个页面)来。

问题分析

这么一个难办的问题,看都看得头晕了,可是要解决问题,还是得用清醒的大脑想问题。

基本思路:保存前面的网页的状态数据,跳转到一个新的页面时,该页面保存前面网页的状态数据,然后在返回时,该网页将原数据取回。

我们先来看一看页面间互传数据该怎么办。

一般而言,都是用 Url 参数的方式,但在这里显然失去了效用, Url 有长度限制,而且这么多的参数,装配 Url 字符串就会让你头疼死掉。

怎么办呢,只好用另外一种办法了:

从一个页面跳转的时候,不要用 Response.Redirect ,而应用 Server.Transfer ,然后再目标网页中使用 Context.Handler ,如下所示:

前一个网页的类是 abc ,后一个网页是 dbc.aspx ,

在前一个网页 abc 中定义公开字段

public string ccc;

那么在 abc 中跳转的时候用

Server.Transfer(dbc.aspx)

在后一个网页使用

((abc)Context.Handler).ccc 就可以取出相应的值了。

但是显然这样做还有一个问题,缺乏通用性。多个页面跳转怎么办?而且 Context.Handler.GetType() 方法是无法使用的,会出现异常。

那就定义接口了。

把有关需要互传的数据定义在接口里,多个网页实现同一个接口。

查询参数、排序条件、要显示的当前页、要显示在当前页的项目 ID ……都定义在接口的实现里。

但问题是,网页在跳转的时候,数据会不断积累,如你从 list1 跳转到 Detail1, 再跳转到 list2 ,再跳转到 Detail2, 这个时候 Detail2 要保存前面三个网页的数据,而这三个网页实现同一个接口,这样可能会导致数据覆盖的情况。

一般而言,面对这种情况的时候是设计一个堆栈似的的数据结构,每进入到一个新的网页时,都把原网页的数据推入堆栈,这叫做保护现场。

可惜这也不能用,因为用户并不会一定就会原路返回,如果用后进先出的方式的话,会发现某个网页要取回自己的数据却发现无法取回了,能取到的不是自己的数据。

只能用数组了,而且这个数组还要是动态的。

那就把刚才的接口修改一下,改成一个类 DataInfo ,然后再实现一个接口,接口里有一个动态数组,用来存放 DataInfo 。

可是动态数组的元素是 object ,范围太广,我希望要严格一些,只允许存放而不是杂乱无章的堆在数组里,要是真的在接口里就实现一个动态数组,那你往里面塞 DataInfo ,我往里面塞别的东东,那还不乱套了。

那就再动态数组外面包装一下,使得它只允许存放 DataInfo 。不能直接在接口中包装,因为我要加入一些代码来校验存放进去的是否只是 DataInfo ,还是其他的什么东东。怎么办呢,那就再自定义一个类 DataInfoList 。并且这些类都要标记为 Serializable ,可序列化,这样才可以在 ViewState 中保存状态。

(未完待续)

Published At
Categories with Web编程
Tagged with
comments powered by Disqus