首先,我们需要着重介绍一些概念,以给你提供一些使这个“奇迹”得以发生的组成部分。太轻易地泄露伏笔对于讲故事来说不是个好的形式,所以那些不愿意在一开始就看到就结局的人就应该在这里先走开一下。 这个综合体的第一部分是定宽字体的使用。Internet Explorer 和Netscape的两种标准定宽字体分别是 Courier New和Lucida Console。大部分的网页设计都不是完全使用标准字体,但它们是这个特定技术 工作的必要条件,正象上面两个画面所显示的,它们看起来不错。
进一步解释,定宽字体就是一种字体,对这种字体的所有字符来说每个字符所占据的空间都是相同的。所以你可能想要采取一定的步骤以确保,这些定宽字体中的某一个或另一个已经安装在组成你的目标观众的客户机上。如果要用你的浏览器看看目前这些字体是否已经安装,在IE5中到Tools | Internet Options下,在Internet Options窗口的General 标签上点击 Fonts 按钮。你就会看到一个名为 Plain Text Font的区:
这就是当前安装的全部定宽字体的列表。对于Netscape 4 和更高版本,到Edit | Preferences ,在 tree-view中展开 Appearance并选择 Fonts。在那有一个Fixed Width Fonts的下拉列表。
第二部分是将某个特定的字体申请到选项列表中的能力。在IE4及以上版本中,可以用以下方式申请一个字体:
< SELECT name="mySelect" style=FONT-FAMILY: Lucida Console, Courier New; >
对Netscape,你可以围绕选项列表使用Font标记:
< FONT FACE="Lucida Console, Courier New" >< /FONT >
第三部分就是无间断空间&nbsp和管道字符的使用。
在下一部分我将演示一个简单的完全编码的多选项列表的例子。最后一部分将演示在上下文使用数据库调用的多选项列表函数,并将探索一些可能的参数选择。
基本的多栏选项列表
以下的HTML例子示范多栏选项列表的最基本形式:
< HTML >
< HEAD >
< TITLE >Simple Multi-Column Select List< /TITLE >
< /HEAD >
< BODY >
< FORM >
< FONT FACE="Lucida Console, Courier New" >
< SELECT name="sel1" size=3 style="HEIGHT:65 px; WIDTH:350px;
FONT-FAMILY: Lucida Console, Courier New; FONT-SIZE: x-small;" >
< OPTION VALUE="1" >
John |Chips |
Intel |Hillsboro
< OPTION VALUE="2" >
Sue |Wotz |
Microsoft |Redmond
< OPTION VALUE="3" >
Nathan |Babb |
Eprise |Framingham
< /SELECT >
< /FONT >
< /FORM >
< /BODY >
< /HTML >
列表选项包含FirstName、 LastName、 Company 和 City。为了正确地安排第一栏的格式,我们必须发现字节数最多的名字''Nathan'',然后为全部7个字符增加一个无间断空间。然后我们给John和Sue增加适当数量的空格,使它成为7个字符,在结尾增加管道符号,现在我们就有了一个格式化了的栏。
要注意我们在以上代码中增加了< FONT > 标记。这是为了在Netscape上使用考虑,因为Netscape不象 IE能够识别字体属性。还要注意< FORM > 标记的出现。这也是Netscape 的要求。IE允许你绘制表单域、 选项列表等,不用将它们放在< FORM > 标记中,而在Netscape ,如果想要提取表单控制的话,就要求 表单的标记。
使用这种方法有明显的缺陷。比如说,不容易向列表中增加一个记录,尤其是当它的大小与编码确定的空间不适合的时候。另外,这种类型的列表难以维护,尤其是当你不是创建编码的那个人的时候。 在下一部分我将向你演示如何用代码动态创建一个多栏选项列表。
我所执行的多栏选项列表是一个叫做aspOptionListMulti()的函数。如果你想跳过解释直接进入代码部分,可以在文章结尾处找到供下载的函数。下载的函数包含以下几个文件:
simpleSelect.htm
multicolumnlist.mdb
example.asp
connect.inc
aspFunction.asp
simpleSelect.htm 是前面第一个例子的代码。我的动态例子使用的数据库是Access ,其数据源是 multicolumnlist.mdb 。通过运行example.asp 脚本就可以看到这个动态例子实际执行情况。这个文件中包含connect.inc 以建立与数据库的连接,aspFunction.asp 进行对aspOptionListMulti() 的函数调用。
如下表中所列的,我的多栏列表函数有10个参数。我不想在aspOptionListMulti()的数据恢复方面多花时间。在接下来的讨论中,假设存在某种类型的数据源,你将在函数中处理返回的行。在这个例子中,我假设我所存储的程序没有参数。我也不再深入解释这个函数是如何调用的。如果你想知道函数是如何被调用的,可以在下载处参考例子。
proc - 生成存储的程序名
parmname - 用于sp调用的可选参数名 (可传递单个值或数组)
parmval - 用于sp调用的可选参数值 (可传递单个值或数组)
val - 选项列表的值栏
selval - 如果是空字符串就不选择。如果它在选项列表中,就作为所选择的值被传递。
column -在选项列表中代表栏的一个数组。
maxcollen - 一个栏的最大可能长度。 -1 就关闭。可以是单个值,对于多栏就是数组。
name - 选项列表的名字。
firstopt - 确定第一个选项是否为空。如果是"|*" 那么就无选项。如果传递了一个值,那么它就是 第一个选项的值(推荐使用0或1)。
style - 字体必须是固定长度(Courier New 或 Lucida Console)。其它字体的参数选择也可以传递。
optattr - 你可能想要包含在选项列表(如函数调用、属性等)中的供选择的属性(完全引用)。
size -1是普通,2或更多是多选(行)。
以下代码是函数声明及它的其它参数。
Function aspOptionListMulti(proc, parmname, parmval, val, selval, column, maxcollen, name, firstopt, style, optattr, size)
On Error Resume Next
对于我们这个特定情况,你可能想要在函数的最开头包含一个On Error Resume Next 语句。把这个语句放在代码中允许代码在发生错误的情况下继续执行(也许在返回的记录集中,代码在某一行中遇到了一个意外的值)。代码中有这个语句,你还可以建立选项列表的内容。缺点是在发生错误的情况,你的应用程序用户将不能直接向你报告错误,也许这个错误会导致下一个错误,使你的调试工作更加困难。
代码的下一个重要部分是浏览器类型的简单测试。当我们最后建立选项列表时,我们需要区分开IE 和 Netscape ,因为两者在指定字体和型号时对选项列表的处理是不同的。为了简单起见,我将不深入到有关浏览器兼容性的代码,因为我还想集中在代码的关键概念上。如果你想知道更多有关兼容性的 问题,你可以参考本文结尾处提供的代码。
strUA = Request.ServerVariables("HTTP_USER_AGENT")
If Instr(strUA, "MSIE") Then
browserType = "IE"
End If
接下来是对数据库进行调用,以恢复我们创建多栏列表时所想要使用的记录集。变量rsSelect 包含返回的设定结果。我是通过一个DCOM 对象spServerObj来实现对数据库的调用的,在数组中传递 参数及其值。但是通过ASP用ADO进行调用也同样简单,在这个例子中用一个简单的选择语句来恢复记录集。
Set rsSelect = spServerObj.callAnyProc(proc, parmname, parmval)
或者象在例子中那样:
Set rsSelect = MyConn.Execute(SQL_query)
然后,我们调整用来容纳多选项列表的栏名的值的数组的维数。你可以在开始时就定义好它的维数,但是我们按照讨论的流程,在这里设定数组的维数。所以,在column参数中传递的元素数就是在 多栏选项列表中的栏数。
Redim finalMaxLenArray(UBound(column))
第一个主要代码区是收集格式信息以正确地创建选项列表。代码区实质上做两件事。首先,它确定我们的最大栏宽。第二,代码要将找到的最大栏宽与maxcollen 参数中传递的任意值进行协调。
与我的例子正相反,你可能希望明确地设置栏宽,而不是在运行的过程中确定最大宽度。代码区的第一半在记录集中循环,找到最大值长度,并将它存储在相应的finalMaxLenArray 索引中。
有一件事需要指出的是你可以将Do?/CODE > 循环安排在外面,For Next循环在里面。这样的方法也许更有效,但是我觉得这样不符合直觉,所以我就没有改动代码。
''Loop for formatting the select list values
For i = 0 to UBound(column)
maxStrLenTemp = 0
finalMaxLenArray(i) = 0
'' Loop through recordset to determine the maximum length string
'' in given column
If Not rsSelect.BOF Then rsSelect.MoveFirst
Do While Not rsSelect.EOF
maxStrLenTemp = Len(Trim(rsSelect(CStr(Trim(column(i))))))
If Not IsNull(maxStrLenTemp) Then
If CInt(maxStrLenTemp) > CInt(finalMaxLenArray(i)) Then
finalMaxLenArray(i) = maxStrLenTemp
End If
rsSelect.MoveNext
Loop
'' If the derived col length is greater than the maxcollen parameter
'' then reset col length.
If IsArray(maxcollen) = True Then
If CInt(maxcollen(i)) < > -1 Then
If finalMaxLenArray(i) > CInt(maxcollen(i)) Then
finalMaxLenArray(i) = CInt(maxcollen(i))
End If
Else
If CInt(maxcollen) < > -1 Then
If finalMaxLenArray(i) > CInt(maxcollen) Then
finalMaxLenArray(i) = CInt(maxcollen)
End If
End If
Next
也许你能注意到,在以上代码中进行了许多整理Trim(exampleVar) 和重塑CInt(examplevar)工作。当你 处理返回的记录集值时,应该将结果进行一下整理,去掉一些额外的空白。养成整理返回值的习惯能 节约许多调试的时间,是个很好的编码习惯。当对两个值进行比较时,将值重塑成所希望的数据类型 也是很好的。因为ASP只有一种数据类型variant,如果不将它们进行重塑的话,简单的比较过程也会变得难以预料。有时候重塑是多余的(甚至不必要)--但是,我的观点是只要有疑问,就进行重塑。
代码的下一部分就是我们实际构造选项列表的地方。现在我们已经有了所需要的格式信息(包含在 finalMaxLenArray中),可以在确定的栏宽的基础上创建选项列表的单独的行。开始我们设置 选项列表的第一部分:
< SELECT id="< %=name% >" class="aspmultiselect"
name="< %=name% >" size="< %=size% >" style="< %=style% >" < %=optattr% > >
这里我们用许多参数设置选项列表的属性,如大小、字体(只对IE有效,可以为定宽字体传递Font 类属性)。你可以使用optattr 参数来规定其它任何属性或列表中所需要的JavaScript 函数。另外,有时候我打乱ASP代码而直接写HTML,而不用ASP的Response.Write 来写出HTML。在保持代码区不被打断这方面,有些开发人员是纯粹主义者,而我却觉得这样带来的麻烦多于方便。
firstopt参数允许我创建一个空白第一选项,其值与我的参数值相等。如果我将值设置为等于|* , 那么就没有空白第一选项。
< % If Trim(firstopt) < > "|*" Then% >
< OPTION VALUE="< %=firstopt% >" >
< % End If% >
然后通过调用MoveFirst Method,将记录集初始化到它的起点。
< %
''Build the select list now that we have formating info.
If Not rsSelect.BOF Then rsSelect.MoveFirst
Do While Not rsSelect.EOF
然后设置一个临时变量来保存我们的返回值。这个值最终就是多栏选项列表中给定行的索引值。
valTemp = Trim(rsSelect(val))
下面我们在栏数组中循环。这个数组包含记录集栏值的名,它被用来为我们的列表返回值。
For n = 0 to UBound(column)
然后我们测试空值。如果值为空,就用空字符串代替它。
If Not IsNull(rsSelect(column(n))) Then
descTemp = Trim( rsSelect(desc(n)))
Else
descTemp = ""
End If
下面我们来确定哪里的栏值长度与我们预先确定的最大长度相关。如果它较大,就将其截断,反之就增加无间隔空格,使字符串成为用finalMaxLenArray()测量的正确长度。
''strDif Can be negative number if maxcollen is in effect.
strDif = CInt(finalMaxLenArray(n)) - Len(descTemp)
If strDif < 0 Then
descTemp = Mid(descTemp, 1, finalMaxLenArray(n) + 1)
Else
For x = 0 to strDif ''0