lotus notes 开发常用方法
一、关于AppendItemValue
试试下面这个简单的例子:
Dim ws As New notesuiworkspace
Dim uidoc As notesuidocument
Dim doc As notesdocument
Set uidoc=ws.currentdocument
Set doc=uidoc.document
For i=1 To 10
Call doc.appenditemvalue("myitem",i)
Next
Call doc.save(True,True )
这个程序用以对当前文档增加10个ITEM,名字都叫myitem,但值从1到10不等。结果如何?用调试方式进行观察,发现:确实增加了10个ITEM,名字都叫myitem,但值却都是1!这与NOTES中的帮助不符。帮助里宣称:
If the document already has an item called
itemName$, AppendItemVa
lue does not replace it.
Instead, it creates another item of the same
name, and gives it th
e value you specify.
^^^^^^^^^^^^^^^^^^^^
从4.6到5.0结果都一样。
二、区分NOTES的前台类与后台类
由于两者的使用范围不一,在写程序时应注意这一点,尤其写代理时。如果在后台服务器运行的代理里加一句:
Dim ws As New notesuiworkspace
代理运行日志报错:Unkown Error.
三、关于NOTES与OLE的共享域
NOTES提供了一个很好的功能:共享域。NOTES用共享域来与OLE应用程序交换彼此信息。但除非确有必要与OLE应用程序共享信息,建议在设计表单时,选上:禁止共享域。
笔者曾在一个表单中创建了一个作者域AUTHOR,又在它的RTF域中嵌入MS-WORD文档:
CALL
UIDOC.CREATOBJECT("MYDOC","WORD.DOCUMENT.8","")
似乎一切都正常。但当我变更了NOTES的作者域AUTHOR(因笔者试图通过作者域的改变来控制NOTES文档的修改进而达到流程控制的目的),因为流程的需要,我把它变成了两个值,在NOTES中显示为:
user1/co1/server1,user2/co1/server2
然后对RTF域中所嵌入的WORD文档进行了修改,然后退出,保存。结果问题出现了,NOTES报错:你不是文档的作者,不能保存!
什么原因?当时我明明是用user1/co1/server1进行修改的!后来,仔细调试,把AUTHOR的隐藏属性去掉,仔细观察,发现只要激活了RTF域中嵌入的WORD文档,在RTF域中退出来时AUTHOR的值竟变成了:
user1/co1/server1/co1/server2!
原来,WORD也有一个作者域,名字也叫AUTHOR(似乎是不能改变的)WORD的AUTHOR与NOTES的AUTHOR相互影响(怎么影响?我也不知道),于是变出了上面的这个怪东西!解决办法,当然,就是把NOTES表单“禁止域交换”了!(OLE应用程序有太多的域,想弄清这些域的名字,似乎不太可能。所以“禁止域交换”应是解决这类问题的最好办法了,窃以为)
四、使用NOTES整合OA应用、OFFICE弥补NOTES不足
Sub Entering(Source As Field)
Dim curws As New notesuiworkspace
Dim uidoc As notesuidocument
Set uidoc=curws.currentdocument
lnflag=uidoc.fieldgettext("docadd")
If lnflag=0 Then
Call uidoc.fieldsettext("docadd","1")
Call
uidoc.createobject("worddoc","word.document.8","")
End If
Exit Sub
End Sub
当提交后,须记录并显示编辑者对WORD文档的所有修改,故而要将WORD文档改成修订状态。在提交按钮中,写如下程序:
Sub Click(Source As Button)
Dim ws As New notesuiworkspace
Dim uidoc As notesuidocument
Set uidoc=ws.currentdocument
Dim curdoc As notesdocument
Set curdoc=uidoc.document
Dim worddoc As notesembeddedobject
Dim wordapp As Variant
Call ws.editdocument(True)
lnflag=uidoc.fieldgettext("docadd")
If lnflag="1" Then
Set worddoc=curdoc.embeddedobjects(0)
Set wordapp=worddoc.activate(False)
Call worddoc.doverb("编辑(&E)")
wordapp.application.visible=False
wordapp.application.activedocument.trackrevisions=True
wordapp.application.activedocument.showrevisions=True
Call wordapp.application.activedocument.save
Call wordapp.application.exit
End If
Call uidoc.save
Call ws.editdocument(False)
End Sub
上述修改WORD文档为修订状态的那段程序,其实可以改为:
Set wordapp=uidoc.GetObject("worddoc")
wordapp.application.visible=False
wordapp.application.activedocument.trackrevisions=True
wordapp.application.activedocument.showrevisions=True
Call wordapp.application.activedocument.save
即通过UIDOC的GetObject方法进行访问。
但通过OLE对象访问时,应注意拼写的正确性,尤其是能过指明OLE对象名称(而不是文件名称)来创建时,更应注意OLE对象名称的拼写正确
性。否则,错误难以预料。例如,在RTF域的Entering事件中将创建对象语句按如下修改:
把: Call
uidoc.createobject("worddoc","word.document.8","")
改为: Call
uidoc.createobject("worddoc","word.document","")
OLE对象还是可以创建,一切似乎都很正常。但是当执行提交时,若通过EmbeddedObject的访问文档,则在Set
wordapp=worddoc.activate
(False)时出错:不能储存对象。
若通过UIDOC的GetObject方法进行访问,则在
wordapp.application.activedocument.trackrevisions=True时出错:没有激活文档。
五、使用APPENDTOTEXTLIST
APPENDTOTEXTLIST增加文本列表项。使用方法如下:
Dim rtitem as notesitem
dim uidoc as notesuidocument
dim ws as notesuiworkspace
dim curdoc as notesdocument
set uidoc=ws.currentdocument
set curdoc=uidoc.document
set rtitem=curdoc.getfirstitem("authors")
lcreader=curdoc.getitemvalue("readers")
for i=0 to ubound(lcreader)
call rtitem.appendtotextlist(lcreader(i))
next
上例把多值域READERS的值逐个追加到另一个多值域AUTHORS中。要实现这种目的,还有一种办法,即:通过NOTESDOCUMENT的GETITEMVALUE方法
分别把两个域的值放到两个数组中,再合并两个数组到一个新的数组中,通过调用NOTESDOCUMENT的REPLACEITEMVALUE方法把新数组的值赋给
AUTHORS。但显然这种方法,编程量要大得多。
也许有人会说,为什么不用字符串的相加,再通NOTESUIDOCUMENT的FIELD
SETTEXT把相加后的字
符串赋给相应的域?按NOTES的说明及其相应的帮助来说,这应该是可以的(当然,两个字符串之间的分隔符应用设计域时指定的分隔符分
开)。但实际上,这种方法极不可靠!尤其是两个域的值均为姓名时,实在无法保证这样运算产生的结果是怎样的。NOTES对姓名域有自己的处
理办法,但它怎样将前台显示的姓名转换成后台的姓名?只怕没人能搞得清楚。本人有试过这种方法。当只有一两个值时,这种运算一般不会
错;但当值多时(我试的时候有7个左右),有时一切正常,有时出现的错误不可思议:NOTES居然把整个字符串当做一个值!它竟然根本没有理
会域的多值分隔符!还要注意一点,不须通过NOTESDOCUMENT的SAVE方法,上例所做的变更依然有效。NOTESITEM的变量应是指向相应文档的指针
(我猜的)。
六、NOTES的ODBC支持缺陷
A NOTES无法支持后台数据库(如ORACLE)的预储程序Stored
Procedure,即使新版的6.2也是如此。尽管帮助自称有提供运行后台数据库SP的函数(resultset.execprocedure),但其实并不起作用。
B 如果在一个odbc connection中有多句SQL的话,会出错:too many
cursor。尽管你的sql语句根本不含cursor。Lotus自称在4.6及以后版本中,这个bug已解决,不过,答案也确是如此
七、NOTES的数组
A 定义数组
有两种方式:DIM和REDIM。
DIM定义的是固定个数、数据类型的数组;而REDIM则不同,它可以定义不同类型的数据,也可以定义个数并非固定的数据。比较下面几个例子。
都合法的例子:
Dim myarray(5,2) as string Redim myarray(5,2) as
string
前者错误而后者合法的例子:
n=10 n=10
Dim myarray(n) as string Redim myarray(n,2) as
string
另外REDIM还可以定义未定类型的数组,如:Redim myarray(10)
B 数组个数
在以DIM或REDIM定义数组时指定的下标,表示的是访问该数组时所容许的最大下标,却不是该数组的个数。实际上,一维数组个数总是等于(最
大下标+1),访问时是通过下标从0开始逐个访问的。
比如:Dim myarray(5) As
String定义的数组元素有6个,分别是:myarray (0)、
myarray(1)、myarray(2)、myarray(3)、myarray(4)、myarray(5)。
再如:Redim thisarray(2,5) As String
实际上定义了一个(2+1)*(5+1)=1 8的二维数组。
既然如此,那么,可不可义定义一个只有一个元素的数组呢?答案是:不可以。
如前所说,Redim thisarray(1)定义的数组实际上有(1+1)个数组元素,但类似于:
Redim thisarray(0)的语法,NOTES又认为是错误的。所以,
不能定义一个只有一个数组元素的数组。其实,以上说的只是其默认状况。其实,定义数组可以通过定义下标的起止从而达到定义数组的个数甚
至下标的起止编号的。比如:Redim thisarray(1980 to1990)就
定义了一个含有11个元素的数组,下标从1980到1990。
C 关于UBOUND函数
UBOUND返回的是一维数组的最大下标,而不是元素个数。比如:Dim Myarray(5) As
Integer,那么UBOUND(Myarray)返回的值是5,而不是6。
UBOUND也可以应用于二维数组。应用于二维数组时,它返回的是第一个下标的最大值。
比如:Dim Myarray(6,3) As Integer,
那么UBOUND(Myarray)返回的值是6,而不是7,更不是18(6*3=18)。
若要返回第二个下标的最大值,则使用:UBOUND(Myarray,2)。
与UBOUND相对应的是另外一个函数:LBOUND,它返回数组的最小下标。与UBOUND类似,LBOUND(Myarray,2)则返回数组MYARRAY的第二个下标的最
小值。所以,准确地说,一维数组Myarray的元素个数为:UBOUND(Myarray)-LBOUND(Myarray)+1,而二维数组的元素个数则为:
(UBOUND(Myarray)-LBOUND(Myarray)+1)*(UBOUND(Myarray,2)-LBOUND(Myarray,2)+1)
多维数组依此类推。
D 返回数组的函数
可以定义一个函数,使其返回数组。宣告函数时只要宣告它返回Variant型即可
。如下例:
Function db_string(Byval fdname As String) As
Variant
fdnum=Len(fdname)
Redim lcarray(fdnum,2) As String
lcarray(0,0)="thisstr"
lcarray(0,1)=","
lcarray(0,2)=","
......
db_string=lcarray '使函数返回数组lcarray的值
End Function
在调用函数时,以如下方式调用:
thisstring="AAAAAAAA"
Dim Thisarray As Variant
thisarray=Db_string(thisstring)
print thisarray(0,0)
八、
NOTES的ODBC:(LS:DO)
A
使用LotusScript编写ODBC程序时需要掌握的几个类:ODBCConnection、ODBCQ
uery、与
ODBCResultSet。通常做法为:
Set con = New ODBCConnection
Dim dbqry As New ODBCQuery
Dim dbresult As New ODBCResultSet
ret=con.ConnectTo(DBSOURCENAME,DBUSERNAME,DBPASS)
If con.isconnected=False Then
ret1=Msgbox("数据库无法连接,请洽系统管理员",48,"提示信息")
odbc_insert=-2
Exit Function
End If
dbqry.sql="select * from THISTABLE where
THISTABLE.ID=THISID"
Set dbqry.Connection = con
Set dbresult.query=dbqry
dbresult.execute
%REM
通过dbresult访问关系数据库:取值、更新数据库
%END REM
status=dbresult.close(db_commit)
ret=con.disconnect
B
通过ODBCResultSet更新数据(插入、删除、修改),可以有两种方式。一种方式如上例,对ODBCQry.SQL赋为查询语句,然后能过
ODBCResultSet类的ADDROW、UPDATEROW、DELETEROW
及SETVALUE等方法更新关系型数据库。如下例,对THISTABLE增加一笔记录,并赋字符串型
字段field1的值为test:
Set con = New ODBCConnection
Dim dbqry As New ODBCQuery
Dim dbresult As New ODBCResultSet
ret=con.ConnectTo(DBSOURCENAME,DBUSERNAME,DBPASS)
If con.isconnected=False Then
ret1=Msgbox("数据库无法连接,请洽系统管理员",48,"提示信息")
odbc_insert=-2
Exit Function
End If
dbqry.sql="select * from THISTABLE where 1=0"
Set dbqry.Connection = con
Set dbresult.query=dbqry
dbresult.execute
Call dbresult.addrow
Call dbresult.setvalue("field1","test")
Call dbresult.updaterow
status=dbresult.close(db_commit)
ret=con.disconnect
由于updaterow方法只有对只含一笔记录的ODBCResultSet才有效(多笔时会报错),故而在SQL中的条件中只有一个永远不成立的条件:1=0,以
保证在执行addrow之后在updaterow 之前ODBCResultSet中只有一笔。
另外一种方式是直接用SQL语句更新数据库。它的好处在于一次可以更新
多笔,而且比较灵活,坏处则在于必须考虑DOMINO与后台关系数据库之间的数据类型的转换。如下例,可以达到与上例一样的效果。
Set con = New ODBCConnection
Dim dbqry As New ODBCQuery
Dim dbresult As New ODBCResultSet
ret=con.ConnectTo(DBSOURCENAME,DBUSERNAME,DBPASS)
If con.isconnected=False Then
ret1=Msgbox("数据库无法连接,请洽系统管理员",48,"提示信息")
odbc_insert=-2
Exit Function
End If
dbqry.sql="insert into THISTABLE (THISID) values
('test')"
Set dbqry.Connection = con
Set dbresult.query=dbqry
dbresult.execute
status=dbresult.close(db_commit)
ret=con.disconnect
C 关于类型转换
通过ODBCResultSet更新数据时通常要进行类型转换。通过上述的方式一中的方法更新数据
库可以不用类型转换即可更新数据库,条件是:
NOTES表单中的相应域值不能为空(因为在
NOTES中任何域值若为空,则其数据类型总是string,值为空字符串)。如何实现?将方式
一的例子
中的
Call dbresult.setvalue("field1","test")改造如下:
ltmp=Doc.GetItemValue("doc_field1")
Call dbresult.setvalue("field1",ltmp(0))
其中,doc_field1为关系数据库字段field1对应的NOTES域名。如此改造,即可通过域名更
改相应的后台关系数据库了。 通过上述方式二更新
数据库,则要进行类型转换。可以通过前台NOTES的域的 数据类型来
判断生成可以正确执行的SQL语句,例如:字符型,则在生成时在其值的前
后加单引号;数值型,则不必。但如何取得数据类型呢?通过NotesItem的Type属性访问可返回NotesDocument的条目的数据类型,例如:字符型
时,返回1280;数值型时,返回768;日期时间型,返回
1024,等等。但当相应的域值为空时NotesItem.Type永远返回1280(即字符型),而不管
实 际该域值类型为数值还是日期。
还有一种方法进行类型转换,通过后台关系数据库的数据类型生成可以正确执行的SQL语句。
ODBCResultSet.FieldNativeDataType方法返回后台数据类型的相应字段类型,如:
SQL_CHAR,SQL_INTEGER,SQL_TIMESTAMP等等。