PFC编程.docx
《PFC编程.docx》由会员分享,可在线阅读,更多相关《PFC编程.docx(39页珍藏版)》请在冰豆网上搜索。
PFC编程
第19章PFC编程
19.1PFC概述
PFC是由Sybase公司提供一些由源代码组成的基本类库,该类库中提供了在项目开发时经常使用的一些功能,这些功能可以直接以特定的方式使用。
采用这种方式,不仅可以提高开发效率,而且很容易积累原来的开发成果,并且在代码的重用性、接口和编码的标准化及一致性等方面都得到了规范和提高。
使用PFC进行程序设计虽然有很多的优点,但是对于大多数的中国用户来说中文界面还是比英文界面更亲切,所以还有个汉化的问题,这也需要不少的工作量,但这个烦琐的工作并没有多少难度。
19.1.1理解PFC
PFC是PowerBuilderFoundationClass的缩写,是PowerBuilder附带的一种面向对象的开发框架包。
在PowerBuilder的开发环境中可以查看该包中对象的源代码,但是对该包的修改应该遵循一定的策略,不能任意修改。
按照一定的规则对包进行扩展,可以构建更符合自己商业规则的类库,从而确保以后能够很好地利用当前的开发成果。
对PFC类库的修改规定局限在扩展层,但扩展层的很多对象既是基本类库中对象的子孙又是它们的祖先,从而确保了用户在扩展层的修改可以体现在其他子孙对象中,这样的继承层次对开发人员的扩充提供了很好的支持。
比如,在基本类库中的窗口pfc_w_master是很多窗口(包括w_master)的父窗口,而w_master又是pfc_w_master的父窗口,所以,对窗口w_master的修改可以自动体现在pfc_w_master的其他子窗口中。
该包的功能严格采用PowerBuilder面向对象的技术设计,并且采用了类似于C/S模式的服务方式,通过服务向应用程序提供功能。
开发人员在使用时,首先要初始化相应的服务,然后才能使用该服务所提供的功能。
只初始化需要的服务,这样可以减少计算机资源的消耗,提高程序的执行效率。
19.1.2PFC的构成
PFC是由一系列的PBL库、数据表和例程构成。
PBL库可以分为祖先库和扩展库,数据表用来保存错误信息和安全信息,例程在PowerBuilder安装目录下的pfc\demoapp目录中。
祖先库共有七个,文件名都是以pfc开头。
这些库包含了构成PFC功能的全部方法、属性和代码。
这些库中包含的对象是PFC中及最后在用户应用程序中的其余所有对象的祖先。
这七个祖先库如表19-1所示。
表19-1祖先库
库名
内容
pfcmain.pbl
包含标准类用户对象、窗口对象、菜单和标准的可视用户对象
pfcapsrv.pbl
包含提供应用程序服务的定制类用户对象
pfcdwsrv.pbl
包含提供数据窗口服务的定制类用户对象
pfcwnsrv.pbl
包含提供窗口服务和菜单对象的定制类用户对象
pfcutil.pbl
包含调试应用对象
pfcsecsc.pbl
包含实现安全性扫描器(安装在一个单独的目录中)所必须的对象
pfcsecad.pbl
包含实现安全性管理服务(安装在一个单独的目录中)所必须的对象
扩展库对应于前五个祖先库,这些扩展库包含祖先库的直接后代,共有以下五个扩展库:
● pfeapsrv.pbl;
● pfedwsrv.pbl;
● pfemain.pbl;
● pfeutil.pbl;
● pfewnsrv.pbl。
这些扩展库的目的是为了用户修改PFC类库来更好地为自己的应用服务,或者构建符合自己商业规则的扩展库。
PFC的数据表保存在本地数据库PowerBuilder安装目录下的PFC\pfc.db中,共有表19-2所示的几个数据表。
表19-2 PFC数据表
表名
用途
Messages
错误信息服务
Security_apps
安全服务
Security_groupings
安全服务
Security_info
安全服务
Security_template
安全服务
Security_users
安全服务
如果正确安装了PFC,则在DBProfile中的ODBC联结中可以找到pfc,使用该配置可以和PFC数据库建立联结。
PowerBuilder提供的PFC例程对于学习PFC有很大帮助,可以在学习时参阅。
在Library画板中打开PB目录下的pfc\demoapp\peat.PBL可查看该例程。
学习PFC的重点应该是理解它所提供的服务,这些服务是PFC的实质。
而服务大部分都封装在PFC的定制类用户对象中,如表19-3所示。
在使用服务所提供的功能之前必须首先启动该服务。
一般每个对象都提供了启动服务的
表19-3 PFC提供的服务描述
服务
描述
Application服务
通过服务对象提供错误处理、数据窗口缓存、安全性、调试和事务处理登记等功能
DataWindow服务
通过服务对象用数据窗口控件提供数据处理功能
Window服务
通过函数和时间提供普通的窗口处理
Menu服务
通过函数、菜单项提供普通的菜单处理
其他服务
提供扩展的PowerScript语言的功能
函数,该函数的格式如下:
of_Set(flag)
其中,ServiceName为服务的名称,flag为布尔类型参数,表示是启动(True)还是注销(False)该服务。
比如,要启动PFC中Application的调试服务,可以使用of_SetDebug(True)语句。
关于服务的详细内容在后面将做较详细的介绍,更多的信息可以参阅联机帮助中PowerBuilder->PFCObjectReference->Chapter9CustomClassUserObject的详细介绍。
PFC中包含的对象采用自己的命名规则,他们类似于标准的PowerBuilder约定,如表19-4所示。
表19-4 PFC对象名的前缀含义
前缀
意义
前缀
意义
w_
窗口
u_
可视用户对象
m_
菜单
n_
标准类用户对象
S_
全局结构
n_cst
定制类用户对象
祖先库中的所有对象在这些命名约定的前面加上前缀pfc_,例如:
● pfc_w_master:
表示PFC层中的主窗口。
● pfc_u_mle:
表示PFC层中的可视对象MultiTextEdit。
● u_dw:
表示PFC扩展层中的数据窗口可视对象。
● n_cst_dwsrv:
表示扩展层中为数据窗口服务声明的用户类对象。
● n_tr:
表示扩展层中的Transaction标准对象。
变量的声明一般遵循下面规则:
_variablename
其中,scope的规则和PowerScript中的规则是相同的。
19.1.3PFC的体系结构
在介绍PFC的体系结构之前,首先明确一下客户/服务器的服务模式。
客户/服务器当然是由客户端和服务器端两部分组成,这两部分可以在同一台计算机上,也可以是网络上的两台或者多台计算机。
客户端的应用程序联结到服务器端,并请求驻留在服务器上的数据库管理系统的服务;服务器根据客户端的请求进行处理,然后将结果返回到客户端。
客户端的请求或者是一条简单的SQL语句、一个复杂的计算,或者是数据修改等语句;服务器端的返回信息或者是处理后的结果数据,或者是成功/失败代码。
这样,服务器端和客户端两部分各司其职、分工明确、协同工作,共同发挥各自的长处来完成整个应用程序的功能。
在PFC中,也可以理解成两部分的功能,一部分由PFC类库提供,另一部分通过开发人员开发的程序执行;当需要调用PFC的功能时,PFC以服务的方式提供这些功能。
开发人员需要了解在什么时候启动哪些服务,适时地发送服务请求,根据返回值做进一步的操作,并在适当的时候取消该服务。
这取决于对服务的学习和掌握,在后面将详细介绍PFC提供的服务。
当需要启动服务时,应该有一种简单的启动方法,并且在以后可以方便地引用该服务。
在PFC中提供了一种通用的启动服务的语句格式,即of_Set(flag)。
其中,为服务的名称,布尔类型变量flag为启动或者终止服务的标志变量,启动的服务都可以使用一个缺省的实例变量来引用,对功能的引用都应该限制在该服务中。
例如,下面语句在Application中编写来启动调试服务:
gnv_app.Of_SetDebug(True)//启动调试服务
gnv_app.inv_debug.of_Message("登录错误:
"+gnv_app.of_GetUserID())
上面脚本中inv_debug是debug服务的缺省实例变量,通过该变量引用该服务提供的功能。
在使用服务之前首先判断该服务是否启动是一个比较好的习惯:
IfIsNULL(inv_base)orNotIsValid(inv_base)Then
inv_base=Createn_cst_dwsrv
inv_base.of_SetRequestor(this)
Return1
EndIf
由于PFC采用严格的面向对象的设计技术,服务中变量一般不能直接引用,只能通过服务提供的函数。
PFC提供了of_SetX()和of_GetX()函数,其中X是变量名。
这样使PB能控制谁访问了变量以及变量是如何改变的。
在一些特殊的情况下,PFC也提供了可以直接访问的变量。
PFC中除了关启服务的函数外,还提供了很多以of_开头的函数来完成服务的其他功能,在服务启动以后可以调用。
19.2PFC编程基础
本节介绍如何使用PFC进行编程,以便对PFC有一个感性认识,在此基础上更有利于学习后面介绍的PFC服务。
19.2.1设置Application管理
Application是一个应用程序的入口,采用PFC进行程序开发的第一步也是从这里开始的。
创建应用,然后设置该应用的搜索路径,应该包含如下的类库:
PFCAPSRV.PBL
PFCDWSRV.PBL
PFCMAIN.PBL
PFCUTIL.PBL
PFCWNSRV.PBL
PFEAPSRV.PBL
PFEDWSRV.PBL
PFEMAIN.PBL
PFEUTIL.PBL
PFEWNSRV.PBL
在PowerBuilder安装目录中的pfc目录下可以找到这些库文件,如果应用中包含老版本中已经过时的PFC对象,则还应该包含PFCOLD.PBL库。
然后开始编写脚本。
首先声明一个全局变量:
n_cst_appmanagergnv_app
因为Application对象不能继承,所以只能将应用对象中的事件重定向到PFC中的Application对象的事件上,所以必须声明该全局变量。
该变量的名字必须为gnv_app,因为在PFC的对象、事件和函数中都使用变量gnv_app来引用该应用用户对象。
在Application对象的Open事件中编写如下脚本:
gnv_app=Createn_cst_appmanager
gnv_app.Eventpfc_Open(commandline)
该脚本创建n_cst_appmanager应用对象管理,并触发它的事件pfc_Open。
在应用的Close事件中编写脚本注销n_cst_appmanager,脚本如下:
gnv_app.Eventpfc_Close()
Destroygnv_app
在应用对象的SystemError事件中编写如下脚本:
gnv_app.Eventpfc_SystemError()
上面三个事件的脚本有一个共同的特点,就是它们都要调用n_cst_appmanager相应的事件。
从这里可以看出,n_cst_appmanager接管了Application对象中原来的事件处理功能,这主要有两个原因:
要创建的是PFC应用,而Application是不能继承的。
如果Application能够继承的话也没有必要重新定向事件了,只要使用继承创建一个应用对象就可以了。
在应用对象Open事件的脚本中将事件重定向到了n_cst_appmanager的pfc_Open事件。
所以如果还需要启动其他的服务,就应该在n_cst_appmamanger用户对象的pfc_Open事件中编写脚本,该用户对象在PFC的扩展库pfeapsrv.PBL中。
比如,在应用程序打开主窗口之前首先显示一个Splash窗口,可以在pfc_Open事件中编写如下脚本:
This.of_Splash
(2)//显示2秒钟
Open(w_main)//打开主窗口
再比如,在应用程序运行前需要用户登录,可以在pfc_Open事件中编写如下脚本:
Integerli_return
li_return=gnv_app.of_LogonDlg()
Ifli_return=1Then
This.SetMicroHelp("Logonsuccessful")
Else
MessageBox("Logon","Logonfailed")
Close(This)
EndIf
总之,pfc_Open事件是PFC应用程序打开时必定触发的事件,应该在该事件中编写脚本进行初始化工作,而不是在Application的Open事件中进行初始化。
表19-5是可以在该事件中启动的服务。
表19-5 pfc_Open事件可用的服务
服务
函数
Applicationpreference
of_SetAppPreference
DataWindowcaching
of_SetDWCache
Error
of_SetError
Mostrecentlyusedobject
of_SetMRU
Transactionregistration
of_SetTrRegistration
Security
of_SetSecurity
Debug
of_SetDebug
另外,通常在n_cst_appmanager的Constructor事件中调用相关函数来初始化ini文件、版本信息与公司名称等,以便后面Splash窗口中显示。
这些函数有of_SetAppIniFile,of_SetAppKey,of_SetAppPreference,of_SetCopyRight,Of_SetHelpFile和Of_SetVersion等。
事件pfc_PreAbout,pfc_PreLogonDlg,pfc_PreSplash分别在显示About窗口、登录窗口和Splash窗口之前触发,这些事件给了开发人员更灵活的控制机会。
通常在pfc_Logon事件中编写脚本处理登录的用户信息,或者联接数据库,或者根据用户的权限初始化界面等。
比如,假设应用中事务对象SQLCA为n_tr类型,ini文件中包含除用户名称和口令以外的所有和数据库相联的必需信息,下面是pfc_Logon事件中的脚本进行数据库联接:
Stringls_inifile,ls_userid,ls_password
ls_inifile=gnv_app.of_GetAppIniFile()
//使用ini文件中的database节初始化SQLCA
IfSQLCA.of_Init(ls_inifile,"Database")=-1Then
Return-1
EndIf
//as_userid和as_password是pfc_Logon事件的参数
SQLCA.of_SetUser(as_userid,as_password)
IfSQLCA.of_Connect()=-1Then
Return-1
Else
gnv_app.of_SetUserID(as_userid)
Return1
EndIf
19.2.2创建应用
MDI和SDI应用是PB中的两种类型的应用,它们都得到了PFC的支持。
在PFC中为MDI应用提供了w_frame,w_sheet,m_master和m_menu四个已经定义好的对象,为SDI应用提供了w_main窗口,当创建相应的控件时应该通过从这些对象继承来创建新对象。
在前面章节中提到MDI应用主要由Frame,Sheet和菜单三个对象构成,PFC中提供了已经定义好的这三个对象,只要使用继承创建相关的对象就可以生成PFC的MDI应用。
关于菜单,可以使用继承来创建,也可以完全由自己创建,这取决于开发人员的菜单策略。
一般在相关的Frame菜单项中编写脚本来打开Sheet窗口,脚本如下:
n_cst_menulnv_menu
Message.StringParm="w_products"
lnv_menu.of_SendMessage(This,"pfc_Open")
在w_frame的pfc_Open事件中编写如下脚本来处理消息中的信息:
Stringls_sheet
w_sheetlw_sheet
ls_sheet=Message.StringParm
OpenSheet(lw_sheet,ls_sheet,This,0,Layered!
)
创建SDI应用,首先使用继承w_master创建一个窗口,然后根据开发人员的菜单策略创建菜单和附加的其他窗口。
打开c_nst_appmanager,在它的pfc_Open事件中编写脚本打开主窗口。
PFC采用面向对象的设计技术,所以它提供的函数都应该使用界定符来指明要引用哪个对象的函数,并且在调用这些函数之前应该确保启动了相应的服务。
访问对象中封装的变量也应该使用of_Set和of_Get之类的函数,而不是直接引用。
在使用某些实例变量之前,最好首先调用of_ls函数来判断要引用的对象是否有效。
PFC提供了很多用户事件和预先编写好脚本的事件,这些预先编写好的脚本用来提供控件相应的PFC服务功能,没有提供脚本的用户事件让开发人员编写脚本以便能够更好地使用所开发的应用程序。
在PB中,事件的访问权限总是public,可以使用对象名称加界定符和事件名称来执行事件中的脚本。
可以修改扩展类库中事件的脚本,但是不要在子对象中覆盖这些脚本,这样会影响PFC的服务功能。
PFC提供很多没有脚本的用户事件,开发人员可以根据应用程序的需要在其中编写脚本。
很多这样的事件是通过消息路由在用户点击菜单项时触发的,还有一些是由应用程序中的脚本来触发的。
比如,可以在u_dw控件的pfc_Retrieve中编写如下脚本:
ReturnThis.Retrieve()
当调用PFC的事件时,应该提供适当的参数;被调用事件还有可能调用其他事件或者PFC对象的相关函数。
当然,开发人员也可以直接调用相关的PFC函数,但是应该尽可能地调用相关事件,因为事件中已经包含了相关的错误处理脚本,功能比较完备。
另外,很多PFC对象提供了pre-事件,为开发人员提供了更灵活的控制机会。
这些事件在相关动作执行之前触发,一般都有可以自动实例化的引用方式(reference)的参数,该参数一般是事件所在控件的引用,以便在发生动作之前控制控件的属性。
下面是一些这样的典型事件:
● pfc_PreAbout
● pfc_PreClose
● pfc_PreLogonDlg
● pfc_PreOpen
● pfc_PrePageSetupDlg
● pfc_PrePrintDlg
● pfc_PreRestoreRow
● pfc_PreSplash
● pfc_PreToolbar
● pfc_PreUpdate
比如,在About窗口中可以添加一个附加的信息:
用户标识。
首先看pfc_PreAbout事件的参数为n_cst_aboutattrib类型,这说明需要扩展该对象。
打开类库pfeapsrv.pbl中的n_cst_aboutattrib,声明一个实例变量:
Stringis_userid。
接下来是修改w_about窗口,打开类库pfeapsrv.pbl中的w_about,在窗口上放置一个名称为sle_userid的单行编辑器。
然后在该窗口的Open事件中编写脚本sle_userid.Text=inv_aboutattrib.is_userid,来显示用户标识。
最后,在n_cst_appmanager的pfc_PreAbout事件中初始化该实例变量,编写脚本anv_aboutattrib.is_userid=this.of_GetUserID()。
至此,对about窗口的扩充工作全部完成。
19.2.3使用属性对象
在PFC中提供了很多专门用来保存对象属性的用户对象,这是PFC特有的取代结构数据类型的一种手段。
使用这种方法有以下特性:
● 包含访问权限为public的实例变量;
● 自动实例化;
● 通常以attrib为名字结尾;
● 经常用来向PFC中的pre-事件传递参数,比如pfc_PreAbout;
● 是可以扩充的(可以向该用户对象添加实例变量)。
开发人员可以扩充该用户对象中的成员变量,而结构类型变量不应该由开发人员随便修改,所以使用用户对象取代结构类型数据类型是PFC一种比较方便扩充的选择。
19.2.4使用PFC常量
许多PFC对象中都包含声明为常量的实例变量,使用这些实例变量可以编写可读性更好的脚本。
比如,下面两个脚本实现同样的功能,但是第二种编写方法的可读性就明显好于第一种:
//1=Filterlinkagestyle.
dw_emp.inv_linkage.of_SetStyle
(1)
//FILTERisaconstantinstancevariable
//thatisinitializedto1.
dw_emp.inv_linkage.of_SetStyle(dw_emp.inv_linkage.FILTER)
19.2.5消息路由
PFC使用消息路由来处理菜单和窗口之间的交互。
所谓消息路由是指菜单通过将能完成某个特定过程的消息发送到窗口,窗口通过触发相应的事件来执行相应的脚本。
而开发人员需要知道将要触发的窗口事件的名称,然后调用of_SendMessage函数来触发PFC中的pfc_MessageRouter事件,由该事件选择适当的对象来接收刚才的消息。
通常的开发习惯是直接在菜单项的Click事件中处理相关的操作,这点区别一定要注意。
消息路由函数of_SendMessage()是从PFC菜单中的每个菜单项调用的,它发送要执行的事件名称。
该函数的语法是instancename.of_SendMessage(message),其中instancename是实例n_cst_menu变量的名称;message是一个String类型的参数,用来指明pfc_MessageRouter事件要触发的用户事件的名称。
发送消息后,Pfc_MessageRouter随后按窗口、当前控件、活动的数据窗口控件的顺序激活事件。
如果不想使用PFC菜单,但是仍然想使用消息路由,可以将of_message()和用户可能需要的任何菜单项代码移动到自己的菜单中。
19.2.6PFC的事务管理
PowerScript可以简单、方便地访问数据库,主要是得益于事务对象的中间联络