NotesViewNavigator在Notes对象里常常被忽视,平常的开发实践中似乎只需要NotesView,NotesDocument最多加上NotesViewEntry的组合就够了。不过,有些场合它却可以发挥难以替代的作用。在23. 在LotusScript中利用视图的合计功能求和里我们已经看到一个例子,这里继续说明另一个实例。
联动菜单,一个列表框(姑且称为二级列表框)的选项跟随另一个列表框(一级列表框)的选中项变化。在Notes里实现这样的功能很简单以至可以成为典型,不论是在客户端还是Web开发中。列表框的选项如果不是静态的,往往使用视图里的数据。一级列表框的选项使用@DbColumn,二级列表框则使用@DbLookup,如:
@DbLookup("":"";"";"vhForReport";OfficeCode;2);
其中vhForReport为第一列按照“办公室代码”字段分类的视图;OfficeCode为一级列表框的域值,即对应于视图的“办公室代码”字段;视图的第二列为“年份”值,用于二级列表框Year字段的选项。
但是有一些更复杂的情况,这样简单的公式不幸失效了。假定用于获得选项值的列,也就是上面的第二列因为某种需要也是分类的,@DbLookup公式返回的值就只有这一列的第一个类别(值的个数就等于第一个类别所包含的文档数,如果还有第三列分类,则值的个数等于更细分的第三个分类列的第一个类别所包含的文档数)。按照IBM网站上的说法,这不是bug,而是@DbLookup设计成就是这样。无论这样有没有道理,我们还是要完成功能,总不能把这样的技术问题归咎于IBM,作为实现不了的原因解释给用户听。
有的朋友可能会说,方法很简单,既然是由列分类造成的,那就再建一个视图,取消那一列的分类不就行了。问题确实也解决了,但却不是理想的方案。因为这样仅仅为了一个字段的选项就在数据库里维护了一个视图。如果这个视图包含的文档很多,那么占用的空间和维护索引消耗的资源就更加昂贵。我们知道一个大型的Notes应用往往有几十甚至上百个视图,为了提高数据库的性能,应该尽可能地减少不必要的视图,或者说充分利用每一个视图。在本文所讨论的问题里,如果能够和其他代码或界面共用同一个视图来获得列表框字段的选项,当然更好。
此外当作为数据源的视图包含文档太多时,用@DbLookup技术上也行不通了。因为@DbLookup返回的数据大小有限制,即64KB,在开发帮助文档里对如何计算它有详细的介绍。如果匹配查询的文档很多,需要返回的那一列的值就可能超过64KB。这时候我们也希望能将目标列先分类,然后只返回这些已经唯一化的类别值。可惜我们已经看到@DbLookup无法做到这一点。
这时候NotesViewNavigator就可以帮上我们的忙。
首先在表单上方创建一个YearOptions的多值域,再将Year列表框的选项设定为YearOptions。然后在OfficeCode列表框的选项发生变化时,调用以下函数,设置YearOptions的值。这个函数的主要部分就是创建一个NotesViewNavigator,包含一级列表框OfficeCode选值的所有文档,然后遍历分类NotesViewEntry,取得它的对应列值。
Public Function SetYearOptions
Dim years As New NArray(-1)
Dim view As NotesView
Set view=db.Getview("vhRecord4Report1")
Dim nav As NotesViewNavigator
Set nav=view.Createviewnavfromcategory(doc.OfficeCode(0))
Dim entry As NotesViewEntry
Set entry=nav.Getfirst()
Do Until entry Is Nothing
Call years.Add(entry.Columnvalues(1))
Set entry=nav.Getnextsibling(entry)
Loop
doc.YearOptions=years.container
Call uidoc.Refresh()
End Function