PB详细学习笔记
1.当我们在程序中用修改数据库的时候,只要不用commit切程序在运行,我们此时是查
看不到数据库中当前操作的数据的,只有commit后,或退出该程序时,才能在数据库中查看到修改后的数据。
a)这是因为修改数据的时候是先有一个缓冲区,此时还没有修改到实际的数据,多以
此时没用commit,而用rollback的时候,对缓冲区的修改代码将不被执行;此时
就是起到出错数据回滚的操作
b)但是,当我们修改了数据没用到commit的时候,而是直接退出程序,此时数据库
缓冲区中的修改代码也会被执行,也就是说,只要不用rollback,推车程序时数据
也会被更改。
c)所以,当我们某个地方会出错需要数据回滚时,就要在那里进行rollback操作。
2.在数据窗口对象里设置显示值和实际值时,要注意:当输出文档时任然使用的是实际值,
显示值值是看的,没有实际意义。
1.在我们第一次装PDM时,装数据库的时候数据库是没有程序的内部用户的,所以一般是建一个空数据库,然后进行初始化,这样数据库就有了2个用户了,然后再将有数据的数据库还原上去,但此时该数据库也是没有用户的,就要给它授权,即映射,此时执行Grant的代码就是映射授权。
a)所以,当没有系统用户时,执行Grant是没有用的。
1.我们在pb中,事件是可以继承的,方法是不能继承的:
a)如果子对象中的该方法写了新代码,则调用时是执行新写的代码,如果子对象该方
法中什么也没写,则会调用父对象的该方法
b)所以,可见继承是可以先执行父的再执行子的,方法是不行的
c)这个就叫做多态
1.因为标题名字是字段名字加_t,所以,在自定义字段时,设置字段名时不要有“_”.
1.一定要注意补丁包升级和数据库升级,补丁包升级是将我们的程序进行升级,而数据库
升级是对数据库进行修改操作的升级。
a)二者不是一回事,都要进行操作。
b)我们要区分数据库版本和程序版本,两者要对应才能正常运行。
2.我们调试时用的动态库为PB 文件夹下的动态库,程序运行时用的是Bin目录下的动态
库,程序安装时用的也是Bin目录下的动态库,所以要清楚不同环境下的运行要求。
3.修改数据窗口对象的查询语句时,先剪切再添加,这样更能反映到当前PB中来。
4.在我们触发事件打开窗口的时候,一定要注意打开窗口和当前窗口的关系,如果他们是
继承关系,则会报错的~!
5.在写SQL语句时注意where 1=1 的用法,我们开始就把这个写在一个变量中,当要增加
条件时就直接加在后面。从而不用担心where出现的位置问题。
在PB写代码时,不同的目标可以引用对方的库,所以我们编译时,先编译功能小的库,再编译功能齐全的库。
为什么呢?
比如:我们目标1调用目标2的库1,在库1中调用了许多目标1中的其他库的对象,在编译时就会将这些联系都写在库1.pbd文件中,当编译目标2时也会重新生成库1.pbd,由于在目标2中没有库1中调用其他库的对象,因为目标2只是用到了库1中很小的一部分对象,这时编译并不会报错,所以重新生成的.pbd文件是不全的(一般发生在多个程序集成在一起的情况)。所以,我们只能先编译功能小的目标,再编译功能大的目标。防止相同库先后编译发生覆盖的错误。
1.记住,在分析代码的时候,要分功能模块来看,不要整体的来分析,那样会很复杂而且
不容易想清楚,很容易乱。
a)分析每一小块的代码,知道他的作用和关联;最后将每个块联系起来串一边,就能
晓得当前脚本的功能了。
b)有时候不一定要分析某个函数它内部是怎么操作的,只需要晓得它的返回结果,这
样的函数就不用费劲的去分析代码了。
2.在我们的程序中是有版本的,当定义好了程序中的版本后,就要进行数据库升级,这个
升级就会将版本信息存放到数据库中,只有程序的版本和数据库的版本一致时,才可以登录系统。
3.PB写的代码调试是要依赖与环境的,所以,当调试出问题时,就要把一些支持调试的.DLL
文件拷贝到PB的安装目录下面去。
a)注意考入的.dll文件要和当前版本的程序相匹配,因为不同版本的程序里的.DLL文
件是不一样的。
4.要注意区分程序运行的代码和程序安装的代码,两者是有区别的。
5.我们要注意本地数据源的连接,不是创建了数据源就可以能连的上去,同时也要为数据
源指定数据库文件。否则找不到数据。
6.我们在使用对象的各种类型变量时,一定要注意值的滞留问题,如有这种情况,则要在
开头或结尾将其进行清空。
a)Eg:当有一个数组il_partid[] 时,开始我们循环找到了2个值,放在il_partid[1],
il_partid[2]里面,完成了满足条件的查询。
b)但是当我们下一次检索时,只找到一个满足条件的值,放在了il_partid[1]里,此时
我们查询时,则会出错,出现2个满足条件的值,因为我们值改变了il_partid[1],但是il_partid[2]上次查询结果的值仍然保留在,故会出错。
c)所以,我们在每次执行代码前就要清空:il_partid[] = ll_empt[]
d)这样就避免了出错。
7.我们要清楚事件的触发和函数的调用,当我们不好用某个动作来触发某个自定义的事件
代码时,我们可以将该事件放在另一个事件的结尾用trigger 或post 来触发自定义事件。
a)事件其实就是代码~!
8.注意:在我们用if 判断的时候,如果用= 是表示判断,不是赋值,而用函数返回值判断
时,是要执行该函数再判断的,所以,不要搞混了,要清楚什么时候执行,什么时候表示判断。
9.Modify基本可以修改数据窗口中所有的属性项。
10.我们要修改某对象的源时,可以将其Export 导出源码,然后我们就杂源码的定义部分
修改对象的属性。要知道,我们对源直接修改就是修改对象的本身属性。
11.any 要想知道Any类型变量中保存数据的类型,可以使用函数ClassName()
12.我们要连接多个数据库时,第一个数据库算是默认的,所以在我们用游标或select语句
时都不用using sqlca; 不用就算是默认的。
a)所以,当用多个数据库时,检索数据的时候就要在查询的地方放用using语句,不
然它算是连接到了默认的数据库,则会出错。
b)不管用哪个数据库都是要定义好数据源的,同时在代码中声明连接,这样才能得到
数据。(声明:sqlca.odbc=;sqlca.code;……)
c)在应用对象中声明的是数据源参数的链接,因为数据源和数据库之间的关系在创建
数据源的时候一般会定义好的。
d)所以链接数据库,可看做2个部分,一个是创建数据源,再个是链接数据源。
13.我们用自定义的数据窗口对象时,选择的是外部数据源,此时在画板界面上,是先定义
字段的属性,再增加字段的。
1.要分清PB中对象和类的区别:
a)一般我们将window menu treeviewitem 等就是“类”,专门作为一种数据类型
来定义该类型的实例。
b)而我们继承是在原类的基础上创建一个新类。
c)New是创建新对象,不同与继承。
14.我们的PDM的代码包CODE一般包含2个文件夹:
a)Inte_pbr文件夹:里面存放的是程序运行时需要的所有图片,但是加载图片进去还
不够,它里面还有一个配置文件(INTEMAN.PBR;INTEPDM.PBR等),将每个图片
的路径信息都写在了里面,只有这样程序才能完全找到使用。
b)这个文件夹要安装到C盘下面,这是程序指定的路径。
c)因为在代码中的编译对象中包含了这2个文件夹的路径,所以当编译完成后生成
的.pbd文件就包含了2部分的内容。
d)当没编译只是运行代码时,这个包就要放在指定路径下了,不然运行时图片都不会
显示出来。
15.另外一个就是我们代码的安装包了。
1.当你创建一个工作目标(.pbt)后,它会自动创建一个应用库(.pbl),我们创建和操作的对
象就放在这个应用库下面。
2.层次结构:—工作空间
—目标
—应用库(多个)
—对象(多个)
—目标(多个)
3.一般一个工作目标就是一个应用程序,就是说同一个目标下不同应用库中的对象是可以
互相访问的。
4.同一目标下的不同库中的对象可以相同。
5.不管是同一目标还是不同目标或是不同的库中,我们都可以将有用的对象拷贝到指定的
库中。
6.且一个工作目标里面只有一个应用库中有且只能有一个(应用对象)。
7.我们创建的全局变量,它是在整个目标(程序)中是有效的。
8.应用对象的属性一般具有全局性,所以要慎重设置。
9.我们在PB系统树中创建的对象可以看做是一个实例对象,直接拿出去用,也可以看做
一个类,用它来创建新的对象,新的对象将有原对象的所有属性和事件、方法。
10.不同对象中的控件名可以相同,不同库中的对象名字可以相同,不同目标的库名也是可
以相同的。
11.但是,在一个目标中,不要将库名中的对象名取一样的,这样会出错,因为同一目标下
的对象是可以相互访问的。
1.在应用对象中设置事务对象的连接属性,与在DBprifile中添加DB是没有关联的。
2.当Return属于某个脚本时,就退出该脚本的运行,当它是某个脚本函数的代码时,则返
回到调用该函数的位置。
3.我们要清楚每个函数在各种情况下的用法。它是与控件连一起用的还是单独可以用的,
最好根据函数的功能和意思来分辨。
4.我们要把形参当作实际的值来看待。
5.一定要注意每个函数的功能,以及他们之间的内在联系:
a)例如:getfilesavename()
i.它是打开一个准备要保存的文件路径对话框,它返回路径名和文件名,除此之
外不做其他的操作。
b)又如oleobject的saveas():
i.它是将文件保存到一个指定路径,当这个路径和getfilename()的路径不相符时
或没有传递时,它就会报错而不能保存。
c)又如PB中的saveas()
i.它不关前面的getfilesavename(),它的功能就是打开保存对话框,保存到指定
位置进行保存。
1.我们所用的对象其实就是一堆视图化的代码,当我们想要改变某个对象的属性或内容时,
可以直接编辑他们的源代码:Edit source.
2.当我们在定义函数时,规定了返回类型,则它会要求你返回一种类型。
所以,如果不想返回值,则在定义的时候在返回值上填(none)
1.我们用户定义事件其实分2种情况:
a)通过事件ID 调用windows已经存在的事件。此时各项参数都是设置好的,只需要
设置名字。
b)将事件ID设置为(none),此时我们就可以自己设置事件的各项参数了,此时可以
说才是真正的用户事件。
2.在pb中触发事件有2种方法:
a)通过我们手动的操作,来触发相应的事件。
b)通过TriggerEvent()或者Postevent()来触发。
i.Object.TriggerEvent(clicked!)是立即触发用户指定的事件,在继续调用下面的事
件,属于同步调用。
ii.Object.Postevent(clicked!)是将用户指定的事件放置到指定控件事件队伍中的末尾,等所有的事件都完毕后再执行该指定事件,属于异步调用事件。
iii.Object.triggerevent(‘ue_delete’)当执行的是用户自定义的事件时,则要加上引号,而不是以感叹号结尾。
c)dw_department.event trigger ue_delete()
d)w_main_frame.event post ue_paint()
i.这是CB控件clicked事件的一段脚本。
ii.可见,ue_delete()是dw_department对象的事件,现在是放在CB的clicked事
件中去触发dw_department对象的ue_delete()事件。
iii.可见,事件是属于对象的,所以前面要加限制。
1.他们通过ODBC连接的数据源时,在PB中是通过事务对象连接的ODBC的数据源,
而在我们配置数据源时就定义好了要连接的数据库。
2.我们在PB 中对数据进行的修改操作基本都是对缓冲区进行的。
3.我们要区别缓冲区的数据和数据窗口显示的数据,大多情况缓冲区会直接反应到数
据窗口上,但要注意区分开来。
4.在pb中建的DBfile文件数据库,并不是使我们开发的应用与数据源连接,但是它将
数据库直接反应到PB中来了,我们可以直接在这里对数据库进行操作,而不用跑去修改本身的数据库。
5.记住,我们用事务对象连接的是数据源,DBfile是数据库的显示。
6.所以,我们通过对DBfile的修改来直接对数据库进行修改。
7.我们连接一个DBfile只是可以对其进行操作,同时也必须要连,因为我们处理数据
时要进行语法效验,如果不连的话在保存时会报错。
8.因为在我们没有运行代码时,代码中连接数据库的代码根本就没有执行,故没有连
到数据库中,所以,进行语法效验时是根据DBfile中连接的数据库进行效验的。所以,以后每次都要连上DBfile文件。
1.我们创建数据窗口对象时,会有标题名和内容名,内容名就是我们数据库表的字段名字,
而标题名一般是表的字段名字加上“_t”,,但是这是对象的名字,我们还要给标题的每个对象设置文本内容。
2.别把对象的名字和存在的内容搞混了~~~!
1.用游标读取数据时,读取一行就要用sqlcode进行检查一边:
a)当为0时,这成功读取了当前的一条数据
b)当为-1时,读取错误
c)当为100时,则表示都读取完了
2.当使用游标和存储过程时,十分注意commit和rollback的使用,因为他们将关闭两者的
使用。
3.只要是对象,一般都是可以继承的。
4.菜单的工具栏只能显示在MDI窗口中。
5.我们的全路径是目录\文件名。
6.所以目录是没有“\”的。
1.fileopen()
a)以指定的方式打开指定的(存在或不存在)文件,可读或可写
b)返回句柄
i.Fileopen(filepath,streammode!,write!,lockwrite!,replace!)
此时表示,如果该路径文件已经存在,那么就覆盖该文件。
2.此时一定要注意:
a)当是以写的方式打开(write!)时,它一定会重新创建一个空文件,如果路径已经存在,
且有replace!,那么该空文件就会覆盖指定文件。
b)如果路径不存在,那么就会创建新的指定空文件。
c)当是以读的方式打开时,它不会创建新的文件,而是让你指定要打开的文件。
d)当lockwrite!时,你可以看该文件
e)当lockread!时,你就不能看该文件了
OleObjectWord=Create OLEObject
li_Ret = OleObjectWord.ConnectToObject("","word.application")
IF li_Ret <> 0 THEN
li_Ret = OleObjectWord.ConnectToNewObject("word.application") //没有打开则新建if li_Ret <> 0 then
MessageBox('OLE错误','OLE无法连接,错误ID:' + string(li_Ret)+'可能是Word安装不正确!')
end if
end if
OleObjectWord.Documents.Add()
此时,上面值是连接了操作的对象。只有当OleObjectWord.Documents.Add()时才会打开一个新的文件。此时它就会成为当前活动对象。
1.OleObjectWord.ConnectToObject("实例路径","指定的服务器应用程序")
a)也就是说:实例路径,就是你要操作的文件全路径
b)服务器应用程序,就是支持文件的应用程序。
2.当我们没有实例路径时,它会对当前活动对象进行操作
3.当有实例路径时,它就对指定路径的文件进行操作。
4.OleObjectWord.ConnectTonewObject("指定的服务器应用程序")
1.创建新的对象并连接到该对象上。
此时就要注意了,它创建的应用对象是空的,也就是说它里面连空白都没有。此时就要用OleObjectWord.Documents.Add()来增加操作空间。
1.Saveas()
既可用OLE对象,也可用于数据窗口对象
2者的用法不一样,具体情况具体分析。
用于数据窗口对象时,可以保存数据也可以保存统计图。
1.我们可以直接给一个表的字段定义主键,但是给表定义外键时,同时也要指出是对应的
哪个表的主键。
2.先找主键,在去联系它的外键。
3.我们在调用外部函数前对其声明时,记住:一个声明要写成一行。
function int FileExpand (string InputFile, string OutputFile) library "intedll.dll" (一行) subroutine FillMany (int serial_no, ref string password) library "intedll.dll" (一行)
1.我们的事件一般都是定义好的,当触发该事件的时候,就执行该事件中的脚本,我们是
在脚本中写代码,调用函数。
2.当是继承对象后,它里面的事件和函数都会继承过去,且不可修改,当继续加代码时,
它会先执行原有的代码,再执行新写的代码。
1.if keydown(keyenter!) then
if this.text="" then
return
else
uo_ok .setfocus()
uo_ok.triggerevent(clicked!)
end if
end if
可见,触发的按键以!来区分
要出发事件时,也是用事件名+!
1.响应式窗口一般是提示且必须执行完才会继续执行的窗口,所以,当它的事件执行完时,
我们在代码结尾用close()来关掉它。
2.要知道各种值的保存时间:
a)实例变量:当实例对象关闭时,变量下次重置
b)全局变量:当程序结束下次再开时重置。
3.注意事件的触发条件,如果不满足就给它加代码加以限制。
4.if isnull(ls_newcode) or trim(ls_newcode)='' or trim(lower(ls_newcode))='null' then
ls_newcode=''
a)注意判断为空的几种情况。
b)Null要加引号用。
1.注意用户对象的使用:
a)例如标准可视用户对象,它其实就是一个控件,但是我们使用它的时候它会在窗口
界面创建属于该对象的控件,而不是对象本身。所以,千万注意对象名和你创建的
对象控件名字。
b)要理解这样就可以重复使用了。
2.注意可视对象和不可视对象引用的方法:
1.getitemstring()、getitemnumber()
a)它是得到数据窗口中(主缓冲区)某项的数据,注意类型
2.Setitem()
a)它是给数据窗口中(主缓冲区)的某项赋值,针对的是缓冲区的。类型为Any类型。
3.Settext()
a)它是给浮动数据窗口赋值,而不反应到缓冲区里。其值要与所在列类型相兼容。
4.当用setitem()赋值后,要反应到浮动数据窗口中,这要移动焦点或在代码中调用
Accepttext()函数。
5.Accepttext()是将漂浮在数据窗口中的内容放到缓冲区里去。(必须通过有效验证。)
a)注意它的用法,当焦点从漂浮数据窗口中的一项移到另一项时,系统自动会将漂浮
状态的数据反应到缓冲区。
b)但是,当焦点从当前漂浮数据窗口移到其他控件时,则漂浮数据是不会反应到缓冲
区的,所以,我们一般在数据窗口的Losefocus()事件中用到此函数。
c)记住:设置焦点一般是以对象为单位的。
d)不要在Itemchanged或ItemError event中编写Acceptext()函数,因为Acceptext()函数
有可能驱动
6.ItemChanged或ItemError event,这将造成死循环的出现.
GetText():读取编辑控件的文字.
7.String(date,{fomate})
a)将数据以指定格式转换成字符串。
b)String(ll_id,’00’)
i.If ll_id=1, return ll_id 则ll_id=01
8.insertrow()
a)dw_1.insertrow(0)它是在最后一行后再插入一行。
b)dw_1.insertrow(row)是插入到第row行,就是在第row行前面再插入一行,也就变
成了第row行了。
c)返回值是行号。
9.注意对象的继承层次:
a)当dw_2继承dw_1后,就有了它的所有事件和属性,而且,我们可以对dw_2中
的事件和属性继续修改。
b)当dw_3继承了dw_2后,dw_3就有了dw_2和dw_1所有的事件和属性,此时用
到dw_3时就会出现层次性,看见不同层次时的对象代码。
c)我们创建了的用户对象,将它丢到窗口中用时,其实就是一种继承,继承过来的对
象就有原对象的所有事件和属性。继承后的对象就是一个新的对象了。
1.数据窗口对象类型:Grid时,它的列排列顺序是不能改的。
2.environment le_env 环境对象
getentironment(le_env)
用这个函数,应用程序能够得到当前运行的操作系统、使用的CPU类型、操作系统的版本、屏幕的大小和颜色数等信息。
成功返回1.
3.profilestring(文件名(包含路径),section,key,default)
a)section:指定要得到值所在的节。
b)key:指定要得到值的名称(理解值的名称,其实就是包含某个值的变量名)
c)default:指定的文件、节名、项目不存在时,函数返回该参数指定的值。
d)成功返回指定的key值,错误返回default的默认值。
e)就是:文件名,节名,值名,默认值
4.grouser = create u_user
if Not IsValid ( guo_user ) then
halt close
return
end if
可见,我们在创建用户对象时,最好是判断它是否创建成功,很容易找到错误。
1.getapplication()
a)得到当前应用对象的句柄,通过句柄来查询或修改应用对象的属性。
b)Application app 先定义一个应用的变量
App=getapplication()
App.toolbartips=false
c)也可以直接:getapplication.toolbartips=false
d)可知,应用对象中修改工具栏属性和修改字体属性可以影响到全局窗口风格。
1.this.hide()
iuo_timer = create u_timer
guo_ge.uf_get_toolbar_profile("w_main_frame", this)
PostEvent("ue_open")
注意这段代码,这的窗口w_main_frame中open()事件的一段脚本。
A.其中,this就是代表了窗口w_main_frame,但是一定要注意参数传递的数据类型,
同时也要注意引号的恰当使用:(“”)。
B.W_main_frame本身代表了窗口,但是给它加上引号后,那么它传递的就不是一个窗
口数据类型了,我们知道窗口本身就是一种数据类型,而不是一个字符串。只有this 传递的才是一个窗口。
C.所以,一定要注意参数的类型和引号的使用。以及它本身代表了什么意思。
a)如:as_window_name=”w_main_frame”
b)我们可以this.text=’1’
c)也可以w_main_frame.text=’1’
d)但就是不能as_window_name.text=’1’
e)因为as_window_name它是一个字符串类型,而不是对象类型。
f)所以,在使用数据或类型时,一定要注意类型的匹配。
D.Datawindowchild 是子数据窗口对象,也是一种数据类型。
1.opensheetwithparm(对象1,string,对象2,位置,放置方式)
a)对象1:要打开工作表的窗口对象。Eg:w_main_child 没有引号
b)String: 不定,会将这个值保存到消息对象的Message.stringparm中。
c)对象2:主窗口
d)位置:就是将打开工作表的标题放到倒数第二个菜单中,作为选项。
e)放置方式:打开工作表放置主窗口的位置。
2.传递的数据就只有3类型:
a)数值类型Message.DoubleParm
b)PowerObject(比如结构) Message.PowerObjectParm
c)字符串类型Message.StringParm
3.传递的参数就保存在Message的这3个属性中,在子窗口中就可以将传递过来的数据保
存下来。
1.当我们定义函数时,在选择传递参数的类型时,注意传递参数的返回值,当是值传递时,
他们是互不影响的。
2.当是reference 时,则表示是地址传递,他们的值是相互影响的。他们的变量名不一样,
但是他们的值是一样的。且相互影响。
3.注意:我们在代码中用到的字段名子,是数据窗口对象内容的标题名字,而不是标题的
名字,更不是标题内容的名字。
4.动态SQL语句:
ls_sqlsyn_id = "select pv_objid from dm_psetvalue_" + string ( ll_psetid ) + &
" where pv_code like"+trim(ls_cucode)+" AND "+ " (pv_objid<>"+string(ll_partid)+")"
PREPARE SQLSA FROM :ls_sqlsyn_id;
DECLARE lcr_cur_id DYNAMIC CURSOR FOR SQLSA;
OPEN DYNAMIC lcr_cur_id USING DESCRIPTOR SQLSA ;
do while true
fetch lcr_cur_id INTO :ll_partid;
if sqlca.sqlcode = 0 then
ll_cnt_id ++
ll_id[ll_cnt_id] = ll_partid
elseif sqlca.sqlcode = 100 then
exit
else
guo_ge.uf_warn_db_error()
close lcr_cur_id ;
return
end if
loop
close lcr_cur_id ;
一定要注意类型,当是string时则拿出来直接写,当不是string时,则强制转换后拿出来直接写。
1.我们定义的用户对象可以包含属性,事件,函数:用户对象的属性,其实就是它定义的
实例变量。
2.我们不用返回数据closewithreturn()函数,它会出现不可知的问题,我们一般在它的某个
事件中用Close()将它关闭,同时在的close()事件中用:
a)Message.powerobjectparm=powerobject 来传出数据。
b)同时一般将数据类型定义成实例类型,这样就可以在不同对象中使用
了。
3.我们用openwithparm()函数时,一定要注意打开窗口的类型,当打开的是response!相应
式窗口时,则它一定会执行完openwithparm()函数的所有事件和代码才会继续执行下去。
4.而打开的窗口是其他类型的窗口时,则会在打开窗口的同时喜剧执行下面的代码,所以
要注意窗口的类型和代码执行的时间。
5.在我们脚本中定义数组的时候,不要将同类型的数组名和变量名写成相同的。
6.我们用distinct时,一般只对查询的第一字段使用,其他的都不需要。
"select distinct code,name,partid from tb_gygck"+ &
" where code like " + "~'%" + ls_code_name + "%~' or name like " + "~'%" + ls_code_name +"%~'"
1.我们一定注意值为空(null)的情况,当一个null值的变量取判断条件是否成立时,它
都会满足条件的。:
a)Setnull(code) code>0 或者code<0 都会判断其成立的。
2.对于新的数据窗口,开始时是没有行的,你要用:
for i = 1 to ll_total
ll_row = dw_data.insertrow( 0)
dw_data.setitem(ll_row, 'partid', al_partid)
dw_data.setitem(ll_row, 'ord', ll_ord[i])
所以,要用插入一行来赋值。
1.当我们在做查询窗口数据的时候,一定要在可以多次查询控件的前面加上
datawindow.reset()。
因为,如果数据不清掉,在多次查询的时候会产生数据的叠加。
1.我们一定要注意“树控件”,我们加载了一个树控件后,里面是什么都没有的,此时我们
就要建treeviewitem 定义树视图节点,然后在将该节点加载到树控件中,这样才是真的树功能了。~
2.每个树都有唯一的一个树根,及tree.level=0,表示树根,其他的都算是节点。
3.创建树根和树节点方法一样,只是level表示的值不一样。
4.一定要注意定义树的变量:treeviewitem ltv_current
a)我们把它想成一个结构体类型的缓冲区变量。
b)当我们对这个变量的属性定义好后,用Insertitemfirst()就能将这个变量作为一个
节点实例化,同时返回该实例的句柄。此时我们在取修改该变量是不会改变原句柄
节点的,除非再次用Setitem() 才能对该节点操作,用insertitemfirst() 只会实例化
另外一个节点。
5.Constructor事件:该事件在控件创建时触发
6.选中该事件主要用来插入第一层TreeViewItem项,这样用户一进入检索界面,第一层信
息项就会出现在控件中。
7.ItemPopulate事件:该事件在某TreeViewItem项第一次展开时触发,触发的同时系统会
将该TreeViewItem项的句柄通过参数handle传递过来。
8.此时就要理解了,因为打开程序就会触发Constructor事件,我们在他里面的最后如果用
了expanditem()的话,则如果有子节点就会自动展开。此时,如果我们在ItemPopulate 事件中设置了子节点,则因为Constructor事件中的expanditem会自动展开,此时马上触发该事件,而且会根据是哪个节点触发了该节点的展开事件从将其句柄传过来,所以一定要注意代码和事件执行的流程。
Handle() Return value
Long. Returns the handle of objectname. If objectname is an application and previous is TRUE, Handle returns 0 if there is no previous instance.
If objectname cannot be referenced during execution, Handle returns 0 (for example, if objectname is a window and is not open).
可见,只有是可见的实例才有句柄。
Pb中动态调用函数
目前流行的大部分应用程序中都提供了Undo功能,在PowerBuilder中也可以利用Undo()函数实现该功能。Undo()函数可用于DataWindow, EditMask, MultiLineEdit, RichTextEdit和SingleLineEdit 对象,如果只对某一个对象进行Undo操作,只需在Undo菜单项的单击事件中键入如下脚本:Objectname.undo(),但是当窗口中有多个对象,我们在编写脚本时并不知道要对哪个对象执行undo()操作
如何解决这一问题呢?在PowerBuilder中,undo()等函数只能用于可视对象,而所有可视对象均继承自系统对象类GraphicObject。
因此我们可以定义一个GraphicObject对象的实例变量go_object,等到运行时再用getfocus()函数确定具体操作对象。然后用Typeof()函数确定当前对象的类型,再用Choose case语句根据不同的类型引用不同的实例变量,代码如下:
记住,用go_object=getfocus() 得到的是当前得到焦点对象的引用。
graphicobject go_object
DataWindow dw_object
EditMask em_object
MultiLineEdit mle_object
RichTextEdit rte_object
SingleLineEdit sle_object
go_object=getfocus()
choose case TypeOf(go_object)
case DataWindow!
dw_object=go_object
dw_object.undo()
case EditMask!
em_object=go_object
em_object.undo()
case MultiLineEdit!
mle_object=go_object
mle_object.undo()
case RichTextEdit!
rte_object=go_object
rte_object.undo()
case SingleLineEdit!
sle_object=go_object
sle_object.undo()
case else
messagebox("Error","Can not undo")
end choose
其实我们可以用动态调用函数的方法简单地解决这一问题(只需三行代码),即对GraphicObject对象调用undo()函数,然后在函数名前加上关键字Dynamic。因为对象类GraphicObject并没有undo()这个对象函数,如果不加关键字Dynamic,编译时就会出现错误。使用了Dynamic关键字,PowerBuilder在编译时不检查该函数和所用参数的有效性,而到脚本运行时才去检查该函数。代码如下:
GraphicObject go_object
go_object=getfocus()
go_object.dynamic undo()
1.parentwindow.post dynamic event ue_whole_input ()
a)这是一个目录的单击事件,可见,此时这个目录还没有依赖窗口,所以就要用动态
的触发以后依赖窗口中的事件。
2.当我们在数据窗口对象中给每个字段的backcolour填写了表达式时,一般就会在clicked
事件中对数据窗口的每行显示出来,但是,有的时候会显示不出来,那是因为没有在数据窗口对象中给相应的字段设置Taborder顺序值,此时,他们的order值是0的,故相当于没有焦点,相当与没有做操作。
3.同时,当我们的某个字段是下拉列表或数据窗口时,点击时会出现下拉框,为了避免这
种情况,我们也可以将相应字段的order值设为0 ,此时只会显示当前值了。
1.在我们在数据窗口中给某个字段设定下拉列表或数据窗口时,可以设置实际值,和显示
值,注意其中的关联:
a)比如说,要显示某个人的名字,但是在数据里是用id来表示它,我们检索出来的
也是id,但是我们要得到它的名字,我们就可以考虑用该字段关联相应的数据窗口
对象,注意:字段关联的是数据窗口对象而不是数据窗口。
b)所以,我们新建一个数据窗口对象,选择2个字段,id和name ,用id作为实际值,
用name 作为显示值。
c)本来这个数据窗口显示的值为5,用了5后,这个数据窗口对象就根据5来找到对
象的人名将其显示出来。
d)实质上实际数据是没有变的,但是相当与用了2个数据窗口对象查询语句。
2.一定要注意在使用数据窗口的列名来取值时,这个列名一定是数据窗口对象的内容名,
因为从数据库中建数据窗口对象时,当一次引用2个表时,他们的字段名字都会改变,变为表名加字段名,所以一定要注意。
3.我们进行连表创建数据窗口对象时,他们其实就是一个select查询语句,连表时,就是
用一中关联将他们联系起来,这样就会显示对应的值
比如:表1中的id 唯一,但是这个id创建了另一个表的多个use_id
表2中的user_id
所以,当我们连表时,就给这2个字段指名关系,则在查询时,就会显示对应的数
据值。
我们对数据窗口用retrieve()语句,其实就是执行的数据窗口对象的sql语句,所以,能检索什么出来主要看数据窗口对象的本身的sql语句。
1.重点:
在用动态select 语句时,一定要分清类型,此时的select语句中的字段是不用象查询时要在变量名前加冒号的(:).
ls_filter = " where ( ctsq_createtime >= '" + string(ld_begindate,"yyyy-mm-dd") + "' and ctsq_createtime <= '" + string(ld_enddate,"yyyy-mm-dd") + "' ) " +”and user_id=”
string(llong_id) + “and uer_name=” + ls_name
2.从上面就要看出,这个select的动态语句,本身就是string类型的,所以不管是上面变
量,都要现变为string类型才能写入,其次,表的字段名字是没有修饰的,而且类型任意,所以,而此时字段的类型就要和变量的类型一致,因为开始的string的操作值是为将其能够写入这个string类型的语句中,而在后台操作时,它就是一条实际的查询语句,类型就要匹配
3.所以,其实这是2个层次的类型匹配:
a)一个是语句的string类型的匹配
b)一个是字段属性的匹配。
1.datawindowchild ldw_child
2.dw_data.getchild("cllx" ,ldw_child) “cllx”是指定子数据窗口所在的列。
ldw_child.settrans( sqlca)
ldw_child.retrieve( ) 事先就检索出来。
注意,getchild()得到的是数据窗口对象的引用。
此时,就要注意,当我们在主数据窗口中引用子数据窗口时,不管主的是不是有sql语句:
1)要注意,在子数据列上能否自动检索到数据有2中办法:
a)在数据窗口对象Edit画板中有Autoretrieve选项,够上后就能自动检
索到数据,但是前提是它与事务对象连接了,因为这个选项只是起到
检索的作用。
b)可以利用上述2. 中的getchild得到子的引用,再进行连接并检索。
c)此时如果主的与事务对象slqca 相连,则子数据窗口不用与事务对象
相连也能显示出数据。
d)如果主的没有与事务对象相连,但是子数据窗口与事务对象相连了,
则也会显示出数据。
3.注意:当我们建的数据窗口对象是没有sql语句时,则我们不用使其对应的数据窗口
与事务对象相连,因为与事务对象相连就是能检索到数据,能检索到数据就是因为有select的sql语句,本身就没有sql语句,那么用事务对象也没用,此时,我们有几种办法给其赋值:
i.一般在脚本中用setitem()给其赋值。
ii.利用下啦数据框或下拉列表来赋值。
iii.有数据窗口对象时,则动态建sql 查询条件语句用:dw_result.setsqlselect(select)
iv.没有数据窗口对象时,则先建数据窗口对象(源),再建sql查询条件。
4.我们知道,数据窗口对象本身是源,但是功能就象一条sql语句,当数据库连接好后,
在数据窗口对象画板中就能看到这条slq语句查询出来的数据了,但是当其与数据窗口联合使用时,要想显示数据就必须使相应的数据窗口与事务相连,然后再retrieve()。
5.当我们使用动态的sql语句时,即dw_result.setsqlselect(select)时,就是为了数据窗口分
配了查询条件,只要数据窗口对象相应的字段属性匹配,就可以检索出相应的值。6.但是,我们设置的动态sql只能设置where的条件,即检索出相应条件的数据条件,而
不能改变每个字段本身的属性,因为字段本身的属性是数据窗口对象创建时源的代码定义的,我们所建立的动态sql语句只是查询条件。
7.我们可以理解为,当没有给数据窗口分配数据窗口对象时,我们动态的分配查询语句给
它,是不够的,因为没有显示数据的载体,此时首先给他动态的创建数据窗口对象的源(实质就是创建一个没有sql查询条件的数据窗口对象),其次再给它建sql查询条件,即dw_result.setsqlselect(select),这时就相当与给它动态的分配了数据窗口对象,此时数据窗口连接事务对象再用retrieve()就可以检索到相应的数据。
8.同时,我们要注意,再动态建立sql语句时,如果有了数据窗口对象,我们只会建立好
where条件,但是如果没有数据窗口对象时,我们建立的sql 查询语句就要根数据窗口对象的源代码相对应。也就是说:源中有name字段,sql语句中也要有name字段,如果没有,则都没有。
9.因此,我们得到一个结论,动态的创建数据窗口对象时一般没有创建sql查询条件,然
后再创建sql查询条件才能完整的显示出数据。
10.我们也可以在创建源时同时创建sql查询条件,就象在new创建数据窗口对象时同时创
建。
11.我们最好是将所有的主数据窗口与事务对象相连。避免子数据窗口没有显示出数据的情
况。
12.为什么加载到列的子数据窗口对象能显示出数据,而不用检索,那是因为在属性页面上
点击了Autoretrieve。
13.我们一定要注意数据窗口对象的名字问题:
i.标题名字
ii.内容名字
iii.内容名字对应的在数据库中的名字。
b)标题名字比内容名字多了_t 。
c)记住,内容名字不一定和数据库中相应的字段名字一样:
i.当只有一个表时,他们是一样的
ii.当是多表联合时则内容名字和数据库名字是不一样的。
d)在数据窗口对象画板中我们就可以看见内容名字和对象的数据库名字,此时就要注
意:
i.当我们对数据窗口进行操作时,即getitemstring()就要用内容的名字来操作代
码。
ii.但是我们是对数据库进行操作时,即象select语句时,就要用对应的数据库的名字来操作。
e)所以,一定要注意名字问题,同时注意多表联合使用的情况。
14. //动态数据窗口
数据窗口对象语法:
ls_syntax = sqlca.syntaxFromSql('select kind,name from
tab_t','style(type=tabular)',ls_err1)
dw_1.create(ls_syntax,ls_err2)
dw_1.setTransObject(sqlca)
dw_1.retrieve()
15. //在程序中动态设定列的编辑风格为下拉数据窗口(DropDownDataWindow)
//假设所设定列为部门号"department_id",相关连的子数据窗口为"d_dddw_dep",//显示列为部门名称"dept_name",数据列为部门号"dept_id",实现方法如下:
dw_1.Modify("department_https://www.360docs.net/doc/8c5802132.html,=d_dddw_dep ")
dw_1.Modify("department_id.DDDW.DisplayColumn='dept_name' ")
//或:
dw_1.object.department_https://www.360docs.net/doc/8c5802132.html, = "d_dddw_dep"
dw_1.object.department_id.DDDW.DisplayColumn = "dept_name"
//注:PowerBuilder有一个小工具DWSyntax(程序名为:dwsyn60.exe),提供了获得及修改数据窗口、列等的各项属性值的语法,对编程非常有帮助。上述脚本在DWSyntax 中都能找到。
16. //如何将COLUMN的显示风格在EDIT、DDDW、DDLB之间相互切换:
(1)切换成DDDW:
dw_1.Modify("#https://www.360docs.net/doc/8c5802132.html,='dddw_jg'")
dw_1.Modify("#1.dddw.DisplayColumn='name_jg'")
dw_1.Modify("#1.dddw.DataColumn='id_jg'")
(2)切换成DDLB:
dw_1.Modify("#1.ddlb.case='any'")
dw_1.Object.#1.Values ="red~t1/white~t2"
(3)切换成EDIT:
dw_1.Modify("#1.edit.case='any'")
dw_1.Modify("#1.edit.AutoSelect='Yes'")
(4)获取当前风格:
dw_1.Describe("#1.Edit.Style")
(5)如果还不行,可能得要如下操作:
dw_1.Modify("#https://www.360docs.net/doc/8c5802132.html,=''")一下;
17. //如果符合条件,则显示灰色的背景,否则白色;
本方法同样可以设置该列的字体颜色:其中"column_name"为列名。
dw_1.object.column_name.background.color =
"16777215~tif(fromid='string',rgb(192,192,192),rgb(255,255,255))"
也可以是一行都变色:
dw_1.object.Datawindow.detail.color =
"16777215~tif(fromid='string',rgb(192,192,192),rgb(255,255,255))"
18. //超链接,连到指定的网站。
Inet linet_base
GetContextService("Internet", linet_Base)
linet_Base.HyperlinkToURL('https://www.360docs.net/doc/8c5802132.html,')
Destroy(linet_base)
19.
20.//如何取出DDDW中的Display Column的内容。
dw_1.describe("Evaluate('lookupdisplay(column_name)',1)")
//column_name=列名,'1'表示第一行;看看Help中的Describe
21.我们在对MDI窗口时,打开时直接放中间用一个函数,当改变窗口的大小时,就要在其
Resize()事件中写一个函数来使其按比例缩放,即改变窗口大小,同时也要改变窗口中相应控件的大小,做到比例一致。
22.//怎样得到字符串中汉字的个数
For i = 1 to Len(aString)
ls_ch = Mid(aString,i,1)
If Asc(ls_ch) >= 128 then //判断是否是汉字可以用它的ASCII码是否大于127来判断。
li_num++
i = i+1
End if
Next
23. 补空行:分组表示,每多少行插入一空行来分组。
在窗口的open事件中写如下代码:
long li_count,li_i
li_count=dw_1.retrieve()
if mod(li_count,25)<>0 then //求余
for li_i=1 to 25 - mod(li_count,25)
dw_1.insertrow(0)
next
end if
24.
1.我们一定要注意实例变量或实例数组的值的滞留问题,所以,一般在相应的地方赋空。
2.Dde 是动态数据交换服务器函数。
3.我们一定要注意不同对象的实例变量的区别,特别是在调试时注意:
a)当在代码中跳进一个窗口函数时,窗口对象定义的实例变量和这个函数中用到的实
例变量是一致的,因为这个函数本身就属于这个窗口。
b)但是当我们在代码中跳进一个用户函数时,此时里面所有的实例变量都是属于该对
象的,特别注意用户对象中定义的变量都是以i 开头的,很容易以为是窗口对
象中定义的实例变量,有时候名字都会一样,此时就一定要注意了,这是不一样的,
要区别。
4.子对象继承父对象时,用Call可以引用父对象的属性和方法
5.当一个主窗口中,放了一个一级自定义对象,一级中又有二级自定义子对象,此时就会
有3个层次的机构,此时,大的都可以对小的属性和事件进行操作,但是小的却不能对大的属性和事件进行操作。
https://www.360docs.net/doc/8c5802132.html,mit:
a)当你对数据库的值进行修改或增加的时候,在PB中要进行提交。可以看做是将数
据库中的数据进行保存操作。
b)所以,修改一次,一般就作一次Commit,多次修改,就多次提交。这样就避免中
间修改了,后面要用到该数据时进行修改时而找不到正确的数据。
7.当我们在PB中进行一次修改数据的时候,数据库中也会做修改,但是没有commit的数
据在数据库查看不到,但是PB中可以将修改后的数据查询出来,而不是修改前的;
a)就是说,当再次修改没有commit的数据时,操作的是上次修改后的数据,而不是
原始数据,即使没有commit。
b)当修改后进行commit时,就可以在数据库中查看到了。
c)但是当commit修改后的数据后,就不能进行rollback了。
d)所以,我们可以看作PB设置了一个缓冲区,将修改的数据放在缓冲区里,一旦
commit就将数据更新到数据库中。
e)一旦rollback又回到原始数据里了。
8.当我们读取INI文件的属性时,一般先做判断。
a)当用profilestring时,当找不到文件、或找不到节、或找不到值名时,就会返回默
认值。
b)当出错时才返回空串,当有NULL时返回NULL
c)当没有节时我们可以用setprofilestring()增加节、值名。
9.当一个函数或事件中,可以有多个不用写的默认参数,如果想改掉后面的默认参数,那
么前面的默认参数必须写,不然会出错。
10.记住,当PB在代码中跑的时候,它会依赖PB目录下考过去的DLL文件,所以,由于不
同的企业DLL是不同的,所以换企业测试时一定要换掉DLL文件,否则调试下会出错。
a)而我们用程序跑的时候是不依赖于这些的,因为这些文件都打包在安装包里了
b)类似的还有图片包。
11.注意:当使用对象继承时,方法和事件都会继承过去。
a)但是,对于事件,我们知道对象的事件一般是一样的,所以如果希望继承后的对象
不会触发该事件,则可以通过右键菜单将该事件的属性进行设置,这样触发该事件
时就可以不执行父对象的代码,而是执行新写的代码。
b)系统默认事件继承的代码都会先执行父对象的代码,再执行新写的代码。
c)但是继承的方法不同,它不会出现这种情况。但是要注意方法的重载。
12.一定要注意在用Message传值的时候的位置和状态。
a)一般都要放在close()事件里传消息,记住close()事件在closequery()后面执行。
b)同时还要注意继承关系:当子窗口是继承的时候,一般是先执行父亲的代码在执行
自己的,所以要注意父亲的代码影响子窗口传的消息对象的内容。
13.列表控件类似树控件,都是定义节点,但是列表控件可以将Lable的值显示在多个column
上。
14.我们要注意各个事件或函数的参数情况:
a)如:retrieveend(long rowcount),它的参数rowcount是指被检索出的行数,它的返
回值也是被检索出来的行数。是行数,不是第几行。
b)Retrieverow(long row),它的参数row是指正在被检索的行号,返回值也是正在被
检索的行号。
c)可见,retrieveend()的rowcount在执行过程中是动态的,故可以用于进度条来显示。
15.当面我们检索数据时:
a)如果检索的表之间某些字段有联系,则可以select字段from多表where字段联系。
b)但是如果要检索的数据所在的表之间没有联系,即有些数据分放在多个表中,一次
要从多个表中取数据,此时就要将多个表的数据相加起来显示。这时我们就要用到
多次相加查询语句:select1字段from多表where字段联系+ union all + select2字
段from多表where字段联系……
c)所以用union all可以实现多次查询
d)注意:多次查询中的字段一定要相同。
生成结果集
1
2
-------
select 1--结果集1
union all--合并结果集不改变结果集的显示顺序
select 2--结果集2
1、动态修改数据窗口tab order的值:
方法有两中一种是dw_1.Object.columnname.taborder = '0'
另一种是dw_1.Modify("columnname.taborder= '0'")
integer dwcontrol.SetTabOrder ( string column, integer tabnumber )
dw_1.modify("columnname.protect=0")
或dw_1.modify("#"+string(number)+".protect=0")
2、修改列的属性:
dw_1.object.列1.edit.displayonly= 'Yes '
dw_1.object.列2.edit.displayonly= 'Yes '
3、记住:在数据窗口时,改变tab order的值也会触发失去焦点的一些事件,所以一定要注
意代码的执行位置。
4、This.accepttext()可以放在数据窗口的任何事件中,注意应该在什么时候触发最为有效.
5、代码编译:
a)编译时进行2个过程:
i.一个是语法校验
ii.一个是生产.pbd文件和.exe文件。
b)当语法校验完毕后,开始生产.pbd文件和.exe文件。
c)生产的.pbd文件放在源(library)这边所在的目录里,也是.pbl文件所在的目录;而.exe
文件可以指定存放位置。
d)我们要将.pbd文件和.exe文件放在一起,这样执行.exe文件时可以找到.pbd文件。
e)我们运行时只要.pbd文件和.exe文件即可。
6、当在数据窗口clicked()事件中最后有rutrun1 时,可以去掉运行时数据窗口上点击时的
黑色条框。
7、当我们在创建树的时候,当增加了跟节点,当用到.expenditem()的时候一定会触发该树
的itempopular()事件,所以我们一般在这个事件里增加子节点。
8、在考虑问题是注意有种情况,就是定义的字段分配的空间太小了。导致录不进去。
pb数据窗口常用代码
一、连接数据库 连接数据库也就是指定事务对象。PowerBuilder提供了两个函数:SetTrans()和SetTransObject()。 语法格式: dw_control.SetTrans(TransactionObject) dw_control.SetTransObject(TransactionObject) 其中,dw_control是所使用的数据窗口控件,transactionObject是所要指定的事务对象。 这两个函数有一个重要的区别就是在使用SetTrans()函数时,用户不需做任何数据初始化或事务对象初始化工作。用户只需要在这里填充一个事务对象,PB 就会自动完成对该事物对象的初始化以及和数据库连接的工作。而使用SetTransObject()函数时,用户必须首先把所用的事务对象连接到数据库上。但是,这并不意味着SetTrans()函数比SetTransObject()函数更好,使用SetTrans()函数时,每调用一次函数必须连接一次数据库,因为这个函数在每个事务处理的末端都会执行Disconnect语句。与此相反,使用SetTransObject()函数可以为数据库维持一个开放性的连接。因此在一般情况下,为了提高效率,总是采用SetTransObject()函数。 这两个函数都是成功时返回1,发生错误时返回-1。 二、检索数据 用于检索数据的函数只有一个,就是Retrieve()函数。 语法格式: dw_control.Retrieve() 如果数据窗口控件上的数据窗口对象是有检索参数的,就要在这个函数调用时加上检索参数。而且检索参数必须和数据窗口对象中定义顺序一致。 此函数返回一个长整型的数据,代表检索出来的数据行数。如果发生错误,将返回-1。 三、更新数据 当用户对数据窗口对象内的数据修改后,想把这些修改反映到数据库中去时,必须使用Update()函数。 语法格式: dw_control.Update() 这个更新可能成功,也可能失败。一般在这个函数被调用之后,总是要做一个检查。请看下面的例子: Int li_return
GridView的增删改查
GridView简单的增删改查,自己做了个小项目和大家分享,希望有所帮助 (PS:我也是刚学者,有好的学习方法大家一起学习哈~) 先来贴出本文代码运行的结果: 点击增加一行,第一行出现空格输入内容,点击确定增加,就可以将数据保存到数据库,并绑定到gridview中 点击编辑出现更新和取消,就可以直接在gridview上修改内容了 (PS:编号id是自动生成的,所以不可以修改,在这边让它成为只读的) 前端代码 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>