PFC编程
PFC是由Sybase公司提供一些由源代码组成的基本类库,该类库中提供了在项目开发时经常使用的一些功能,这些功能可以直接以特定的方式使用。采用这种方式,不仅可以提高开发效率,而且很容易积累原来的开发成果,并且在代码的重用性、接口和编码的标准化及一致性等方面都得到了规范和提高。使用PFC进行程序设计虽然有很多的优点,但是对于大多数的中国用户来说中文界面还是比英文界面更亲切,所以还有个汉化的问题,这也需要不少的工作量,但这个烦琐的工作并没有多少难度。
19.1.1 理解PFC
PFC是PowerBuilder Foundation Class的缩写,是PowerBuilder附带的一种面向对象的开发框架包。在PowerBuilder的开发环境中可以查看该包中对象的源代码,但是对该包的修改应该遵循一定的策略,不能任意修改。按照一定的规则对包进行扩展,可以构建更符合自己商业规则的类库,从而确保以后能够很好地利用当前的开发成果。
对PFC类库的修改规定局限在扩展层,但扩展层的很多对象既是基本类库中对象的子孙又是它们的祖先,从而确保了用户在扩展层的修改可以体现在其他子孙对象中,这样的继承层次对开发人员的扩充提供了很好的支持。比如,在基本类库中的窗口pfc_w_master是很多窗口(包括w_master)的父窗口,而w_master又是pfc_w_master的父窗口,所以,对窗口w_master的修改可以自动体现在pfc_w_master的其他子窗口中。
该包的功能严格采用PowerBuilder面向对象的技术设计,并且采用了类似于C/S模式的服务方式,通过服务向应用程序提供功能。开发人员在使用时,首先要初始化相应的服务,然后才能使用该服务所提供的功能。只初始化需要的服务,这样可以减少计算机资源的消耗,提高程序的执行效率。
19.1.2 PFC的构成
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 | 包含实现安全性管理服务(安装在一个单独的目录中)所必须的对象 |
扩展库对应于前五个祖先库,这些扩展库包含祖先库的直接后代,共有以下五个扩展库:
l pfeapsrv. pbl;
l pfedwsrv. pbl;
l pfemain. pbl;
l pfeutil. pbl;
l 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,则在DB Profile中的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<ServiceName>(flag)
其中,ServiceName为服务的名称,flag为布尔类型参数,表示是启动(True)还是注销(False)该服务。比如,要启动PFC中Application的调试服务,可以使用of_SetDebug(True)语句。关于服务的详细内容在后面将做较详细的介绍,更多的信息可以参阅联机帮助中PowerBuilder->PFC Object Reference->Chapter 9 Custom Class User Object的详细介绍。
PFC中包含的对象采用自己的命名规则,他们类似于标准的PowerBuilder约定,如表19-4所示。
表19-4 PFC对象名的前缀含义
前 缀 | 意 义 | 前 缀 | 意 义 |
w_ | 窗口 | u_ | 可视用户对象 |
m_ | 菜单 | n_ | 标准类用户对象 |
S_ | 全局结构 | n_cst | 定制类用户对象 |
祖先库中的所有对象在这些命名约定的前面加上前缀pfc_,例如:
l pfc_w_master:表示PFC层中的主窗口。
l pfc_u_mle:表示PFC层中的可视对象MultiTextEdit。
l u_dw:表示PFC扩展层中的数据窗口可视对象。
l n_cst_dwsrv:表示扩展层中为数据窗口服务声明的用户类对象。
l n_tr:表示扩展层中的Transaction标准对象。
变量的声明一般遵循下面规则:
<scope><datatype>_variablename
其中,scope的规则和PowerScript中的规则是相同的。
19.1.3 PFC的体系结构
在介绍PFC的体系结构之前,首先明确一下客户/服务器的服务模式。客户/服务器当然是由客户端和服务器端两部分组成,这两部分可以在同一台计算机上,也可以是网络上的两台或者多台计算机。客户端的应用程序联结到服务器端,并请求驻留在服务器上的数据库管理系统的服务;服务器根据客户端的请求进行处理,然后将结果返回到客户端。客户端的请求或者是一条简单的SQL语句、一个复杂的计算,或者是数据修改等语句;服务器端的返回信息或者是处理后的结果数据,或者是成功/失败代码。这样,服务器端和客户端两部分各司其职、分工明确、协同工作,共同发挥各自的长处来完成整个应用程序的功能。
在PFC中,也可以理解成两部分的功能,一部分由PFC类库提供,另一部分通过开发人员开发的程序执行;当需要调用PFC的功能时,PFC以服务的方式提供这些功能。开发人员需要了解在什么时候启动哪些服务,适时地发送服务请求,根据返回值做进一步的操作,并在适当的时候取消该服务。这取决于对服务的学习和掌握,在后面将详细介绍PFC提供的服务。
当需要启动服务时,应该有一种简单的启动方法,并且在以后可以方便地引用该服务。在PFC中提供了一种通用的启动服务的语句格式,即of_Set<ServiceName>(flag)。其中,<ServiceName>为服务的名称,布尔类型变量flag为启动或者终止服务的标志变量,启动的服务都可以使用一个缺省的实例变量来引用,对功能的引用都应该限制在该服务中。例如,下面语句在Application中编写来启动调试服务:
gnv_app.Of_SetDebug(True) //启动调试服务
gnv_app.inv_debug.of_Message ("登录错误:" + gnv_app.of_GetUserID())
上面脚本中inv_debug是debug服务的缺省实例变量,通过该变量引用该服务提供的功能。在使用服务之前首先判断该服务是否启动是一个比较好的习惯:
If IsNULL(inv_base) or Not IsValid(inv_base) Then
inv_base = Create n_cst_dwsrv
inv_base.of_SetRequestor(this)
Return 1
End If
由于PFC采用严格的面向对象的设计技术,服务中变量一般不能直接引用,只能通过服务提供的函数。PFC提供了of_SetX()和of_GetX()函数,其中X是变量名。这样使PB能控制谁访问了变量以及变量是如何改变的。在一些特殊的情况下,PFC也提供了可以直接访问的变量。
PFC中除了关启服务的函数外,还提供了很多以of_开头的函数来完成服务的其他功能,在服务启动以后可以调用。
19.2 PFC编程基础
本节介绍如何使用PFC进行编程,以便对PFC有一个感性认识,在此基础上更有利于学习后面介绍的PFC服务。
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_appmanager gnv_app
因为Application对象不能继承,所以只能将应用对象中的事件重定向到PFC中的Application对象的事件上,所以必须声明该全局变量。该变量的名字必须为gnv_app,因为在PFC的对象、事件和函数中都使用变量gnv_app来引用该应用用户对象。在Application对象的Open事件中编写如下脚本:
gnv_app = Create n_cst_appmanager
gnv_app.Event pfc_Open(commandline)
该脚本创建n_cst_appmanager应用对象管理,并触发它的事件pfc_Open。在应用的Close事件中编写脚本注销n_cst_appmanager,脚本如下:
gnv_app.Event pfc_Close( )
Destroy gnv_app
在应用对象的SystemError事件中编写如下脚本:
gnv_app.Event pfc_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事件中编写如下脚本:
Integer li_return
li_return = gnv_app.of_LogonDlg( )
If li_return = 1 Then
This.SetMicroHelp("Logon successful")
Else
MessageBox("Logon", "Logon failed")
Close(This)
End If
总之,pfc_Open事件是PFC应用程序打开时必定触发的事件,应该在该事件中编写脚本进行初始化工作,而不是在Application的Open事件中进行初始化。表19-5是可以在该事件中启动的服务。
表19-5 pfc_Open事件可用的服务
服 务 | 函 数 |
Application preference | of_SetAppPreference |
DataWindow caching | of_SetDWCache |
Error | of_SetError |
Most recently used object | of_SetMRU |
Transaction registration | 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事件中的脚本进行数据库联接:
String ls_inifile, ls_userid, ls_password
ls_inifile = gnv_app.of_GetAppIniFile()
//使用ini文件中的database节初始化SQLCA
If SQLCA.of_Init(ls_inifile,"Database") = -1 Then
Return -1
End If
// as_userid和as_password是pfc_Logon事件的参数
SQLCA.of_SetUser(as_userid, as_password)
If SQLCA.of_Connect() = -1 Then
Return -1
Else
gnv_app.of_SetUserID(as_userid)
Return 1
End If
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_menu lnv_menu
Message.StringParm = "w_products"
lnv_menu.of_SendMessage(This, "pfc_Open")
在w_frame的pfc_Open事件中编写如下脚本来处理消息中的信息:
String ls_sheet
w_sheet lw_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中编写如下脚本:
Return This.Retrieve()
当调用PFC的事件时,应该提供适当的参数;被调用事件还有可能调用其他事件或者PFC对象的相关函数。当然,开发人员也可以直接调用相关的PFC函数,但是应该尽可能地调用相关事件,因为事件中已经包含了相关的错误处理脚本,功能比较完备。
另外,很多PFC对象提供了pre-事件,为开发人员提供了更灵活的控制机会。这些事件在相关动作执行之前触发,一般都有可以自动实例化的引用方式(reference)的参数,该参数一般是事件所在控件的引用,以便在发生动作之前控制控件的属性。下面是一些这样的典型事件:
l pfc_PreAbout
l pfc_PreClose
l pfc_PreLogonDlg
l pfc_PreOpen
l pfc_PrePageSetupDlg
l pfc_PrePrintDlg
l pfc_PreRestoreRow
l pfc_PreSplash
l pfc_PreToolbar
l pfc_PreUpdate
比如,在About窗口中可以添加一个附加的信息:用户标识。首先看pfc_PreAbout事件的参数为n_cst_aboutattrib类型,这说明需要扩展该对象。打开类库pfeapsrv.pbl中的n_cst_aboutattrib,声明一个实例变量:String is_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窗口的扩充工作全部完成。
在PFC中提供了很多专门用来保存对象属性的用户对象,这是PFC特有的取代结构数据类型的一种手段。使用这种方法有以下特性:
l 包含访问权限为public的实例变量;
l 自动实例化;
l 通常以attrib为名字结尾;
l 经常用来向PFC中的pre-事件传递参数,比如pfc_PreAbout;
l 是可以扩充的(可以向该用户对象添加实例变量)。
开发人员可以扩充该用户对象中的成员变量,而结构类型变量不应该由开发人员随便修改,所以使用用户对象取代结构类型数据类型是PFC一种比较方便扩充的选择。
许多PFC对象中都包含声明为常量的实例变量,使用这些实例变量可以编写可读性更好的脚本。比如,下面两个脚本实现同样的功能,但是第二种编写方法的可读性就明显好于第一种:
// 1 = Filter linkage style.
dw_emp.inv_linkage.of_SetStyle(1)
// FILTER is a constant instance variable
// that is initialized to 1.
dw_emp.inv_linkage.of_SetStyle(dw_emp.inv_linkage.FILTER)
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.6 PFC的事务管理
PowerScript可以简单、方便地访问数据库,主要是得益于事务对象的中间联络功能,缺省的全局事务对象是SQLCA。在PFC应用中可以使用n_tr类型的事务对象,从而能够利用PFC中的事务对象管理功能。n_tr是PFC中的一个用户对象,该对象继承自系统的Transaction对象,并定制了很多的实例变量、事件与函数来实现和数据库系统的联接。该对象提供的函数可以管理和数据库的很多操作,比如数据库的联接、断开联接、提交与回退等。当使用该类型的事务对象时,应该使用n_tr提供的事务对象管理函数,而不是使用原来的事务对象管理函数。比如,应该使用of_connect和数据库建立联接,而不是使用connect。
有两种n_tr的使用方式,或者替换SQLCA,或者作为SQLCA的扩充。当需要不止一个事务对象时还可以结合使用两种方式。两种使用方式的操作方法如下:
l 替换SQLCA:在Application画板的属性对话框中指定缺省的事务对象SQLCA的类型为n_tr(在Application画板中显示属性对话框,并点击Additional Properties按钮,然后在Variable Types属性页的SQLCA单行编辑器中输入n_tr即可)。
l 作为SQLCA的扩充:定义n_tr类型的实例变量,在适当的时候用脚本创建该对象。
作为SQLCA的扩充,需要编写脚本来操作该事务对象。首先是创建事务对象,在适当的事件中编写如下脚本:
itr_security = CREATE n_tr
当事务对象还和数据库保持联接状态时关闭应用程序或者Destroy事务对象,这时是否自动提交未提交的事务,可以使用函数设定n_tr的成员变量ib_autorollback来指定。如下所示:
itr_security.of_SetAutoRollback(False)
初始化n_tr类型的事务对象是在创建对象之后使用该对象之前的必须步骤,下面是一段典型的初始化脚本:
String ls_inifile
ls_inifile = gnv_app.of_GetAppIniFile()
If SQLCA.of_Init(ls_inifile,"Database") = -1 Then
MessageBox("错误提示", "初始化ini错误:" + ls_inifile)
Hlat Close
End If
如果能够正确初始化,就可以调用函数of_Connect和数据库建立联接了。脚本如下:
If SQLCA.of_Connect() = -1 Then
MessageBox("错误提示", "不能和ini指定数据库联接:" + ls_inifile)
Halt Close
Else
gnv_app.of_GetFrame().SetMicroHelp("联接完成")
End If
在扩充父对象的事件或者函数时,经常需要首先调用父对象的脚本,然后根据脚本的返回值决定如何继续执行。使用下面的脚本调用父对象的事件并保存返回值:
result = Super::Event eventname ( arguments ... )
可使用如下语句调用父对象的函数,并保存返回值:
result = Super::Function functionname ( arguments ... )
下面的例子是在u_dw的子对象中编写的,用来扩充u_dw的pfc_Update事件的功能,首先调用父对象的pf_Update事件,根据事件的返回值决定如何执行。脚本如下:
Integer li_return
// 使用相关参数调用父对象的pfc_Update事件
li_return = Super::Event pfc_Update(ab_accepttext, ab_resetflag)
If li_return = 1 Then
// 如果父对象的事件执行成功则调用用户自定义事件ue_WriteLog
li_return = This.Event ue_WriteLog
End If
Return li_return
19.2.8 为应用添加在线帮助
对于一个应用程序来说,在线帮助是一个非常重要的部分,它可以让用户尽快地掌握应用程序的用法。PFC对此也给予了支持,提供了相关的事件和函数。在PFC的应用程序中,有两种方式可以为应用指定在线帮助文件。第一种方式是在n_cst_appmanager或者在它的后代对象中,直接为实例变量is_helpfile赋值;第二种是用脚本指定帮助文件。
经常在Application对象的Constructor 事件中编写脚本为应用指定帮助文件。例如,下面脚本为应用指定帮助文件为c:\eis\eisapp.hlp:
This.of_SetHelpFile("c:\eis\eisapp.hlp")
为窗口指定帮助主题,经常在窗口的pfc_PreOpen事件中编写脚本,为窗口指定详细的帮助主题(该主题位于应用的帮助文件中)。比如,下面脚本为窗口指定帮助主题为1020:
Long ll_helpid
ll_helpid = 1020 // 1020是一个帮助主题的标识
ia_helptypeid = ll_helpid
当需要显示帮助主题对话框时,只需要在窗口的帮助按钮的Clicked事件中编写如下代码即可:
Parent.Event pfc_Help( )
该脚本调用窗口的pfc_Help事件,因为PFC应用中的所有窗口都继承自w_master,所以该事件在所有窗口中都可用。
19.2.9 安装PFC的最新版本
因为在程序开发过程中很有可能对PFC的扩充类库进行修改,当升级PFC类库时应该确保这些修改不被覆盖。所以,在升级PFC库时应该做一定的备份工作。最好的建议是,在进行升级之前首先备份PFC扩展库中的所有库文件(PBL文件),然后进行升级安装。
当前PFC的版本号可以在pfcutil.pbl库文件中的用户对象pfc_n_cst_debug里定义的实例变量中找到。版本号包括三部分,主版本号保存在实例变量PFC_MAJOR 中,副版本号保存在PFC_MINOR变量中,实例变量PFC_FIXES 中保存的是修改号。还可以在PFC的帮助文件中找到PFC的版本号,在PowerBuilder安装目录下的pfc目录中打开readme70.txt可以查看版本号。
19.3 Application服务
Application是应用程序的起点,对于PFC应用程序开发人员来说也应该首先从Application服务开始学起。应用服务是应用程序处理所必须的,它向用户提供了 一种“应用程序对象模型”方式使用继承性的能力。因为在PB中不能继承应用对象,所以应用程序服务就使开发人员能够创建可重用的应用程序过程。
19.3.1 建立Application Manager
Application Manager服务是必须在用户应用程序中创建的一个全局变量。在用户的全局变量声明中,必须声明一个引用变量用于指向Application Manager服务。用户可以使用扩展层服务n_cst_appmanager,但是由于用户可能会用应用程序处理来扩展功能,因此应该从n_cst_appmanager来继承自己的Application Manager。当开发人员建立了自己的后代Application Manager(例如名称为n_cst_russ_appmanager)后,就必须声明一个叫做gnv_app的全局引用变量来保存服务实例的地址,就像下面的代码所示:
n_cst_russ_appmanager gnv_app
当生成Application Manager引用变量后,必须创建实例并将应用程序对象的事件重定向到Application Manager服务。用户的应用程序的Open事件应用像下面这样编写:
gnv_app = Create n_cst_russ_appmanager
gnv_app.Event pfc_Open(commandline)
对于用户打算使用的其他每一个事件,都应该将它们重定向到Application Manager(一定要传递事件参数)。由于Open事件创建了Application Manager,因此应用程序对象的Close事件应该取消它。Close事件应该像下面这样编写脚本:
gnv_app.Event pfc_Close()
Destroy gnv_app
尽管在Application画板中创建Application Manager服务并重定向事件,但是仍然需要调出应用程序对象的属性表。在Variable Types属性页上,将SQLCA数据类型从Transaction改为n_tr,这样就确保了SQLCA是用PFC事务对象创建的。n_tr还提供了几个函数来消除重复性的任务,如联结和断开(of_connect()和of_disconnect())以及事务管理(of_commit()和of_rollback())。除了事务对象外的其他全局变量也可以使用PFC中的相关对象来代替,比如Message可以用PFC中的n_msg来代替。
创建Application Manager后,它就保存了应用程序的全局变量。Application Manager有一些Get和Set函数提供对常用全局数据的访问。如:
l 应用程序.ini文件名;
l 用户.ini文件名;
l 帮助文件名;
l 应用程序的注册键;
l 应用程序的MDI框架窗口;
l 用户的ID;
l 应用程序标志;
l 应用程序的版权信息和版本号。
例如,要设置应用程序的版本信息,则可以使用下面的代码:
gnv_app.Of_SetCopyRight("PowerBuilder Unleashed 7.0")
Application Manager还允许用户实例化两个窗口:w_splash和w_log。这两个窗口一般和应用程序的启动相关联。醒目显示窗口显示了使用用户为应用程序设置的一些全局变量(如版权语句等)。要打开醒目显示窗口,使用函数of_splash()并设置醒目显示窗口持续显示的秒数。
要显示登录窗口并提取用户的ID和口令,只要调用of_LogonDlg()函数即可。该函数执行一系列随打开窗口w_logon而设定的步骤。在打开窗口之前,触发Application Manager的pfc_PreLogonDlg事件,初始化用于在窗口内显示的值,随后显示登录窗口。当单击OK按钮并关闭窗口时,将信息通过pfc_Logon事件发送到Application Manager。事件的参数as_userid和as_password随后就可以用于产生用户的事务对象并联结到数据库。
除了设置全局数据值和生成醒目显示及登录窗口外,Application Manager还允许用户实例化前面提到的服务。
19.3.2 Application Manager服务
表19-6列出了Application Manager服务。
表19-6 应用对象中的服务
服 务 名 称 | 描 述 |
Application Manager | 提供对应用程序层变量和所有其他的应用程序服务的访问 |
Application Preferences | 提供一种方式将应用程序和用户数据保存到INI文件或者系统注册表中 |
Datawindow缓存 | 提供一种存储频繁访问的数据窗口对象的数据的方式 |
调试 | 提供跟踪错误的开发工具 |
错误处理 | 提供显示和记录各种PowerBuilder操作的错误的方式 |
安全性 | 提供管理和保护用户应用程序中对象的方式 |
事务对象 | 提供标准的数据库处理功能。如数据库的联接和事务管理 |
事务注册 | 提供一种方式来跟踪用户应用程序中使用的所有事务对象 |
Application Manager是允许用户访问其他所有应用程序服务的门卫。它必须用于访问许多PFC功能。下面将更深入地剖析两个应用服务。
1.Datawindow缓存服务
数据窗口缓存服务提供了一种方法缓存来自经常使用的数据窗口对象的数据。这样就减少了重复性数据库调用和复制客户机上的数据的需要。每当用户需要在应用程序中的几个位置访问相同的数据时,数据窗口缓存服务就为用户提供了解决方法。一个比较典型的应用,是将应用中所有下拉数据窗口都使用缓存服务来解决。
数据窗口缓存服务n_cst_dwcache在Application Manager中通过实例变量inv_dwcache声明。为了实例化该服务,需要在Application Manager中的pfc_Open事件中加入下列代码:
This.of_SetDWCache(True)
下一步就是使用of_Register()函数注册拥有该服务的数据窗口。数据窗口缓存服务有三种类型的语法,其中最有用的语法为:
gnv_app.inv_dwcache.of_Register(id,dwobject,trans{,arguments})
其中,id是用于惟一地标识缓存数据的字符串值。dwobject是包含要缓存的数据窗口对象的名字的字符串。trans是n_tr类型的事务对象的名字,用于检索在dwobject中设置的数据窗口对象的行为。可选参数arguments为要检索数据窗口对象数据的一个或者多个变量,该变量是最多可以有20个元素的任意数据类型的数组。当调用该函数用于某个数据窗口对象时,系统检索这些数据并用数据窗口缓存服务保存这些数据。例如,为了缓存数据窗口d_customer中的数据可以使用如下脚本:
gnv_app.inv_dwcache.of_Register("Customer listing", "d_customer",SQLCA)
要在应用程序中的其他地方访问这些数据,用户必须调用函数of_GetRegistered()。语法如下:
gnv_app.inv_dwcache.of_GetRegistered(id,datastore)
其中,id是在of_Register()函数中设置的字符串值,用于标识被缓存的数据。datastore是一个n_ds类型的变量,它是由被缓存的数据提供的。在调用该函数后,可以对指定的数据窗口对象执行ShareData()操作。要检索存储在一个例子中的数据窗口缓存中的customer数据,应编写如下脚本:
//实例变量的定义是:n_ds ids_data
DataWindowChild ldwc_child
gnv_app.inv_dwcache.of_GetRegistered("Customer Listing",ids_data)
dw_custlist.GetChild("customer",ldwc_child)
ids_data.ShareData(ldwc_child)
2.错误处理服务
错误处理服务允许用户显示多种错误信息类型的错误消息:SQL、数据窗口、应用程序(例如证实)和系统错误。错误信息还可以显示在消息框内或者PFC的消息窗口内,并可以从数据库的表上生成。此外,错误处理服务还允许用户记录错误并自动发送电子邮件。
为了执行错误处理服务,调用Application Manager的pfc_Open事件中的of_SetError()。如果用户想把所有错误消息集中到数据库的某个表内,就需要将Messages表和PFC演示数据库中的已有数据复制到应用程序数据库中。 然后可以将该应用程序特有的消息添加到Message数据表中。为了让应用程序利用该表,使用下面的脚本:
gnv_app.inv_error.of_SetPredefineSource(trans)
其中,trans为n_tr类型的事务对象的名称,它指向包含Messages表的数据库。调用该函数将在具有所有错误信息的服务内生成一个数据仓库。然后用户就可以选择是用标准的PB消息框显示错误信息还是使用PFC的w_message窗口显示错误消息。要做到这一点可以使用如下脚本:
gnv_app.inv_error.of_SetStyle(style)
其中,style为0表示消息框,为1表示PFC消息窗口。当遇到一个错误时用户可以用下面的脚本显示错误消息:
gnv_app.inv_error.Of_Message(MessageID,Parameters)
其中MessageID表示要显示的消息的字符串(它对应于ids_message中的msgnumber)。Parameters是包含可以在指定占位符放入错误消息内的值的一个可选数组。在用户生成的错误消息的文本内可以设置如下代码:
Value Required!Please enter a value For %s
由于用户对于多个字段可重用该消息,%s用于表示一个占位符,当调用of_Message()时将为该占位符设置一个值。然后代码将是下面这个样子:
String ls_error[]
ls_error[1]= "Customer Name"
gnv_app.inv_error.of_Message("VAL122",ls_error)
错误处理服务允许用户为特定的错误定义严重级别和用E_mail通知。
19.3.3 标准类用户对象
正如用户在Application Manager服务中看到的那样,PFC为许多标准的PowerBuilder不可见类提供子类。用户可以使用这些子类代替标准的PowerBuilder类对象。
表19-7 子类前缀的含义
前 缀 | 描 述 |
n_cn | 联结对象后代 |
n_ds | 数据仓库对象后代 |
n_err | 错误对象后代 |
n_ms | Mail Session对象后代 |
n_msg | 消息对象后代 |
n_pl | 管道对象后代 |
n_tr | 事务对象后代 |
n_trp | 传输对象后代 |
每当需要一个标准的PowerBuilder类实例时就应该使用这些不可见类。
19.4 菜 单
PFC提供了两种类型的菜单:用户的主应用程序菜单(用作下拉菜单)和弹出菜单。PowerBuilder从6.0版开始提供了一个新服务,即Most Recently Used(MRU,最近使用的)对象服务,该服务在MDI应用程序中的File菜单下列出了5个最近使用的窗口。像PFC窗口一样,菜单也提供一个类层次,如图19-1所示。可以看出,菜单层次并不是很深,这与已经知道的菜单性能问题是一致的。记住这点,用户可以将m_frame直接用于应用程序的MDI框架菜单,或者可以从m_master继承框架菜单定制后代。所有菜单也都应该从m_master继承。当开发PFC应用时,一般都应该停留在PFC内(这意味着应该尽量使用PFC对象来开发应用程序)。然而当计算PFC菜单的值时这条规则常常被破坏。菜单包含了大量的有用代码,但其中的许多代码可能并不适合用户的应用程序。因此,用户可以选择创建一个PFC菜单的缩微版本或将已经有的菜单框架包补充到PFC内。
PFC中的菜单继承使用了Shift Over/Down菜单属性来控制菜单项的放置。PFC使用一个消息路由与PFC窗口通信,它提供了相当多的功能使菜单中的代码减至最少。PFC中的菜单包括弹出菜单和上下文菜单。表19-8是这些菜单的描述。
表19-8 PFC菜单对象描述
对 象 | 描 述 |
m_dw | 用于从u_dw继承而来的数据窗口控件的弹出菜单 |
m_edit | 用于特定的标准可见用户对象的带有标准编辑选项的弹出菜单 |
m_oc | 用于从u_oc继承而来的控件的具有OLE控件功能的弹出菜单 |
m_view | 用于从u_lv继承而来的ListView控件的弹出菜单 |
当在某个窗口上使用相应的用户对象时这些菜单将自动起作用。
在PFC菜单中实现的消息路由使用户能够将完成某个特定过程的消息发送到一个窗口。附加条件是菜单只需知道执行的事件的名称即可。消息路由通过使用of_SendMessage()函数搜索合适的对象来接收此消息。
消息路由函数Of_SendMessage()是从PFC菜单中的每个菜单项调用的,它接收要执行的事件名称。路由反过来调用其对应窗口的pfc_MessageRouter事件。Pfc_MessageRouter随后就按下列顺序激活从菜单传递过来的事件:窗口、当前控件、最后是活动的数据窗口。如果用户不想使用PFC菜单,但仍然想使用消息路由,则用户所需做的一切就是将of_Message()和用户可能需要的菜单项代码移动至自己的菜单。
19.5 窗口和窗口服务
PFC包含若干基类窗口,这些窗口应该用做用户的所有应用程序窗口的祖先。有若干服务也是提供附加功能的窗口所特有的。
|
PFC中窗口类层次乍一看起来容易引起混淆。所有PFC窗口的基类是pfc_w_master。图19-2显示了窗口的层次关系。
所有以pfc_开头的对象都包含在祖先库中,其他的对象包含在扩展库中。这意味着从pfc_w_master到w_master再到pfc_w_main的层次结构是由祖先库到后代库再回到祖先库。这样做乍一看起来比较奇怪,实际上有很多优点。因为开发人员对于PFC类库的扩充应该限制在扩展库中,而不应该修改祖先库中的任何对象。但是,如何使对象功能的扩展体现在相关同类的对象中?因为对父对象的修改可以自动体现在子对象中,所以使用继承是最好的解决方法。对扩展库中w_master的修改可以直接扩展祖先对象pfc_w_main等对象,而如果pfc_w_main等对象直接继承自pfc_w_master,那么就得在扩展库中对所有后代窗口做同样的修改(例如,w_main和w_sheet)。通过在层次中插入w_master,那么开发人员就有了一个可以修改的共同的祖先,修改时就不必同祖先库打交道了。
PFC中的每个窗口都提供了既定的功能,创建PFC应用时应该使用继承从这些窗口来创建自己的窗口,这样才能在自己的窗口中使用这些既定的功能。
19.5.1 基窗口服务
窗口是一个非常重要的控件,开发人员往往希望PFC中提供很多的基窗口服务功能。但是,查看PFC中的窗口服务(pfc_n_cst_winsrv),它少得让开发人员出乎意料。主要只提供了一个窗口居中的函数of_Center(),它可以使窗口在用户的屏幕上居中显示。要使用基窗口服务,可以在自己窗口的Open事件中加入如下代码:
This.of_SetBase(True)
This.inv_base.of_Center()
如果希望自己的PFC应用程序中的窗口都居中显示,可以将上面的代码添加到相应窗口祖先类中。
PFC中为窗口提供的功能主要集中在pfc_w_master中,下面对它做详细介绍。
19.5.2 pfc_w_master
PFC基类窗口pfc_w_master能够创建和取消不同的窗口服务。此外它还包含了对菜单消息路由的处理,进一步的CloseQuery事件处理以及由PFC触发的应用程序代码的空用户事件的处理。
创建和取消窗口的服务,可以通过调用of_Set<servicename>函数来实现。比如,启动窗口的调整大小服务可以使用下面的脚本:
This.of_SetResize(True)
菜单消息路由事件pfc_MessageRouter定义在该基类窗口中,是PFC应用程序中菜单和窗口交互的必须事件。该事件接收传递过来的事件参数,并调用该参数指定的事件,调用顺序是窗口的、活动控件的、活动数据窗口的事件。
当窗口关闭时需要保存一些用户没有保存的内容,或者做一些特定的处理工作。PFC提供了这方面的功能,开发人员只需要很少的代码便可实现该功能。pfc_Save是实现该功能的一个重要事件,逻辑工作单元是实现该功能时用到的一个重要概念。
所谓逻辑工作单元是指为了保证代码的重用性和工作的完整性,将完成某工作的代码分割成若干个组成部分,并采用一定的机制保证这些部分的协作来正确地完成该工作。在PFC中提供了逻辑单元服务,可以在适当的时候启动该服务。自修改(self-updating)和逻辑工作单元关系紧密。所谓自修改就是指PFC中将控件的修改功能也封装在控件中,在适当的时候(比如,触发pfc_Save事件时)修改自修改对象中没有保存的工作。
pfc_Save事件完成的功能就是由很多函数和事件来共同完成的,该事件检测是否启动逻辑工作单元服务,如果没有启动则启动该服务,然后执行其他的脚本来触发其他相应的事件,调用适当的函数来完成功能。该事件可以完成如下的功能:
l 保存self-updating对象的修改,比如n_ds DataStore,u_tvs TreeView和u_lvs Listview。
l 保存其他控件的改变。
l 控制修改哪些对象以及它们的修改顺序。
l 保存窗口中其他对象的修改。
虽然该事件的组成和脚本比较复杂,但是使用却很简单。需要保存窗口中的工作时只需要简单地调用w_master窗口的pfc_Save事件,然后判断其返回值来决定执行的情况。表19-9是pfc_Save事件返回值的含义。
表19-9 pfc_Save事件返回值的含义
返 回 值 | 含 义 | 注 释 |
1 | 成功 |
|
0 | 没有修改 |
|
-1 | 接收文本错误 | pfc_Save事件终止 |
-2 | pfc_UpdatesPending执行错误 | pfc_Save事件终止 |
-3 | 校验规则错误 | pfc_Save事件终止 |
-4 | pfc_PreUpdate执行错误 | pfc_Save事件终止 |
-5 | pfc_BeginTran执行错误 | pfc_Save事件终止 |
-6 | pfc_Update执行错误 | pfc_EndTran和pfc_DBError两个事件完成,事件pfc_PostUpdate还没有执行 |
-7 | pfc_EndTran执行错误 | pfc_PostUpdate事件没有执行 |
-8 | pfc_PostUpdate执行错误 |
|
-9 | pfc_UpdatePrep执行错误 | pfc_Save事件终止 |
根据返回值可以判断pfc_Save事件执行的情况。另外,还需要对pfc_Save事件可能触发的其他事件的情况。表19-10是对这些事件的描述。
表19-10 pfc_Save可能触发的事件描述
事 件 | 描 述 | 注 释 |
pfc_AcceptText | 窗口上的自修改对象执行AcceptText函数 | w_master的of_AcceptText函数调用该事件 |
pfc_UpdatesPending | 判断哪个自修改对象有可修改内容 | w_master的of_UpdatesPending函数调用该事件 |
pfc_Validation | 对有修改内容的自修改对象进行校验 | 非PFC的数据窗口对象可以使用of_Validation函数调用该事件 |
pfc_UpdatePrep | 空用户事件,可以编写自己的脚本来扩展逻辑单元的功能 | 当窗口作为一个自修改对象时才有必要扩充该事件 |
pfc_PreUpdate | 空用户事件,可以编写脚本提供附加的校验规则 | 返回1表示执行成功,返回其他任意值就可以终止pfc_Save事件的执行 |
pfc_BeginTran | 空用户事件,可以编写脚本来和数据库系统建立联结 | 同上 |
pfc_Update | 为所有自修改对象执行数据库修改操作 | 对于非自修改对象可以扩充该事件来修改对象。返回1表示执行成功,返回-1表示执行失败。如果返回-1,通过调用函数of_SetDBErrorMsg来创建错误错误消息,这时pfc_DBError事件将自动显示错误信息 |
pfc_EndTran | 空用户事件,可以编写脚本来提交或者回退对数据库的修改 | 根据从其他脚本传递过来的参数来决定是提交还是回退。当然,在其他地方也可以使用提交或者回退语句,但是这里应该是最合适的地方 |
pfc_DBError | 修改错误时该事件显示相应的错误信息 | 如果PFC的错误服务启动则调用of_Message来显示错误信息,否则调用MessageBox函数显示错误信息 |
pfc_PostUpdate | 复位所有已修改对象的修改标记 | 当对pfc_Save事件进行扩充处理其他控件时一定要扩充该事件来复位它们的修改标记 |
pfc_Save该事件看起来可能感觉比较复杂,实际上使用还是比较简单的。在窗口的pfc_CloseQuery事件中只需要简单地调用w_master的事件pfc_Save,并根据其返回值决定下一步的操作即可。
19.5.3 其他窗口服务
除了上面介绍的基窗口服务和pfc_w_master外,PFC还提供另外的一些窗口服务,这些服务可以管理窗口的外观、窗口中的配置信息与设置状态栏等,可以以较少编码实现非常友好的窗口界面,是经常用到的一些服务。这些服务如表19-11所示。
表19-11 其他窗口服务描述
服 务 | 功 能 |
Preference服务 | 用来管理ini文件或者系统注册表中的相关信息 |
Status bar服务 | 状态条管理,只适用于frame窗口 |
Sheet management服务 | Sheet窗口管理,只适用于frame窗口 |
Resize服务 | 调整窗口及窗口上某些控件的大小 |
1.Preference服务
窗口Preference服务允许用户保存和读取包含在ini文件和Windows Registry中的用户窗口设置的值。该服务保存诸如窗口大小、工具栏设置和窗口位置这样的信息。要启动该服务,在窗口的Open事件中使用如下脚本:
This.of_SetPreference(True)
缺省时,Preference服务保存所有的窗口设置信息,虽然这样可以毫无遗漏地保存用户的工作环境,但是这样会降低程序的运行效率。可以只选择保存一些重要的信息,或者提供界面让用户指定要保存的信息。可以调用相关函数来指定保存哪些信息,如表19-12所示。
表19-12 跟踪窗口设置信息的函数描述
函 数 | 功 能 | 函 数 | 功 能 |
of_SetMenuItems | 跟踪菜单设置 | of_SetToolBars | 跟踪工具栏位置 |
of_SetToolBarTitles | 跟踪工具栏标题 | of_SetToolBarItemOrder | 跟踪工具栏项目的顺序 |
of_SetToolBarItemSpace | 跟踪工具栏项目间的间距 | of_SetToolBarItemVisible | 跟踪工具栏项目的可见性 |
of_SetWindow | 跟踪窗口的属性 |
|
|
表19-12所列的函数都有一个类型为Boolean的参数,来指明是否保存数据。通常可以关闭那些不让用户修改的信息,一般工具栏中项目的顺序、间距与标题不让修改,菜单项目也不让修改,可以调用函数of_SetToolBarItemOrder,of_SetToolBarItemSpace,of_SetToolBarTitles和of_SetMenuItems,并将函数的参数设置为False。
使用相关函数指定了需要保存的信息后,在适当的事件中可以调用函数of_Save来保存这些信息。该函数的语法如下:
保存到系统注册表时:instancename.of_Save ( registrykey )
保存到ini文件时: instancename.of_Save ( inifile, section )
其中,registrykey为一个String型参数,用来指明要保存到系统注册表的位置;inifile用来指明要保存到的ini文件名称及其位置,section是该文件中的节。检索这些取值时使用函数of_Restore,该函数的参数和of_Save完全相同。
2.状态栏和工具栏服务
当创建一个MDI应用程序时,应该使用继承从PFC中的w_frame来创建MDI框架,这样可以利用PFC中为frame窗口定义的各种服务功能。
Status Bar服务允许用户在MDI框架的状态栏中设置多个信息面板,包括当前的日期、时间以及内存信息等的显示栏。要启动该服务,在MDI框架的Open事件中编写下面的脚本:
This.of_SetStatusBar(True)
然后使用表19-13所示的函数来显示相应的信息。
表19-13 用于显示信息的函数功能
函 数 | 作 用 |
of_SetGDI() | 在状态栏中显示GDI内存(16位) |
of_SetMem() | 在状态栏中显示计算机可用的内存 |
of_SetTimer() | 在状态栏中显示当前日期和时间 |
of_SetUser() | 在状态栏中显示当前用户可用的内存 |
w_frame的MDI框架后代可以使用的其他增强因素还有Toolbar Control对话框。该对话框允许用户操纵常用的工具栏属性,例如工具栏的位置以及是否显示PowerTips和工具栏中项目的文字。下面脚本可以打开工具栏窗口w_toolbars:
gnv_app.of_GetFrame.Event pfc_Toolbars()
3.Resize服务
当窗口或者标签的大小改变时,它上面的其他控件的尺寸也应该按照一定的比例调整,否则有些控件就有可能显示不下了。Resize服务可以实现对指定控件尺寸自动调整的功能。使用of_SetResize(True)启动该服务,然后指定要调整哪些控件的大小,可以使用of_Register函数。该函数有两种语法格式,比较常用的一种语法如下:
instancename.of_Register ( control, method )
其中,instancename是n_cst_resize类型变量的名称,缺省为inv_resize;control是WindowObject类型变量,是注册要调整的控件;method是调整大小的方式,为String类型或者n_cst_resize常量,指明当窗口的Resize事件触发时以什么方式来调整注册控件的尺寸。可用的值如下:
l "FixedToRight"或者FIXEDRIGHT。
l "FixedToBottom"或者FIXEDBOTTOM。
l "FixedToRight&Bottom"或者FIXEDRIGHTBOTTOM。
l "Scale"或者SCALE。
l "ScaleToRight"或者SCALERIGHT。
l "ScaleToBottom"或者SCALEBOTTOM。
l "ScaleToRight&Bottom" or SCALERIGHTBOTTOM。
l "FixedToRight&ScaleToBottom" or FIXEDRIGHT_SCALEBOTTOM。
l "FixedToBottom&ScaleToRight" or FIXEDBOTTOM_SCALERIGHT。
了解上述各个项目功能的最好方法就是在脚本中逐个尝试运行来看效果。缺省时Resize服务允许将窗口和控件缩小得看不清。为了防止这种情况的发生,可以调用of_SetMinSize()来设置窗口的最小宽度和高度。
19.6 数据窗口服务
数据窗口作为PB的一个核心控件,其功能在PFC中也得到了增强。PFC为增强数据窗口功能而提供了一个u_dw用户对象和一些函数、事件以及服务。要利用这些功能,应用程序中的所有数据窗口控件必须是数据窗口控件用户对象u_dw的后代,并在适当的事件中调用适当的PFC函数或者事件即可。
在PFC提供的数据窗口服务中,Basic服务是最基本的服务,该服务包含了对所有其他数据窗口服务的引用。此外,该服务还提供了若干使用标准PowerScript函数的选择方案,如Modify()和Describe(),提供了许多函数用于访问数据窗口对象值。启动该服务,只需要使用语句of_SetBase(True)。表19-14是数据窗口服务及其解释。
表19-14 数据窗口服务
服 务 | 功 能 | 位 置 |
Basic DataWindow service (ancestor For all other services) | 基本数据窗口服务 | n_cst_dwsrv |
DropDown Search service | 根据输入的一个或者多个字母在下拉数据窗口中自动滚动 | n_cst_dwsrv_dropdownsearch |
Filter service | 提供三种方式过滤数据窗口中的数据 | n_cst_dwsrv_filter |
Find and Replace service | 查找和替换数据窗口中的数据 | n_cst_dwsrv_find |
Linkage service | 提供一种在数据窗口之间建立联系的方法,比如主/从结构 | n_cst_dwsrv_linkage |
MultiTable Update service | 控制一个数据窗口对象内修改多个数据表 | n_cst_dwsrv_multitable |
Print Preview service | 提供普通的打印预览功能 | n_cst_dwsrv_printpreview |
DataWindow Property service | 数据窗口属性服务 | n_cst_dwsrv_property |
QueryMode service | 提供对特定查询实现Query模式的能力 | n_cst_dwsrv_querymode |
Reporting service | 提供增强的报表特征,如添加项目、缩放和打印 | n_cst_dwsrv_report |
Required Column service | 自动检查所有指定的数据窗口列的值是否已输入 | n_cst_dwsrv_reqcolumn |
DataWindow Resize service | 当调整数据窗口控件大小时调整数据窗口对象中控件的大小 | n_cst_dwsrv_resize |
Row Management service | 提供普通的数据行处理功能,如插入、删除和恢复删除等 | n_cst_dwsrv_rowmanager |
Row Selection service | 提供数据行的单选、多选和扩展选择的功能 | n_cst_dwsrv_rowselection |
Sort service | 提供不同的机制对数据窗口中的数据进行排序 | n_cst_dwsrv_sort |
下面为初学者介绍几个常用的数据窗口服务。
数据窗口的Sort服务使用户能够用四种方式指定数据的排序条件,以多种方式进行排序。使用of_SetSort(True)语句启动数据窗口的Sort服务后,可以在适当控件的适当事件中编写脚本来实现排序。比如,可以提供按钮让用户来指定排序条件,还可以让用户只点击数据窗口控件的标题栏(header)来完成,具体采用哪种方式排序可由开发人员的开发风格决定。点击标题栏来排序需要首先调用函数of_SetColumnHeader,函数语法如下:
dwcontrol.instancename.of_SetColumnHeader ( boolean )
其中,dwcontrol为数据窗口控件名称(该数据窗口为u_dw对象的后代);instancename为用户对象n_cst_dwsrv_sort的实例变量,缺省为nv_sort;boolean指定是否以标题栏进行排序。
函数of_SetStyle用来设定要使用哪种方式让用户来设定排序条件。函数的语法如下:
dwcontrol.instancename.of_SetStyle ( sortstyle )
其中,dwcontrol和instancename两个参数与上面的含义完全相同;sortstyle为一个整型参数,它的取值及含义如下:
DEFAULT或者0 PowerBuilder对话框
DRAGDROP或者1 用w_sortdragdrop进行拖放排序
SIMPLE或者2 用w_sortsingle进行单一排序
DROPDOWNLISTBOX或者3 用w_sortmulti进行多个排序
当参数SortStyle取值为3时,数据窗口中的每个数据栏都必须有一个对应的文本对象。下面是一段例程:
dw_emplist.of_SetSort(True)
dw_emplist.inv_sort.of_SetColumnNameSource(2)
dw_emplist.inv_sort.of_SetUseDisplay(True)
dw_emplist.inv_sort.of_SetStyle (dw_emplist.inv_sort.DRAGDROP)
19.6.2 查找和替换服务
当需要为用户提供在数据窗口中查找或者替换内容时,使用PFC的查找和替换服务可以大大节省编码的工作量。首先使用of_SetFind(True)来启动该服务,然后可以编写脚本来激活该服务。例如:
If IsValid(inv_find) Then
inv_find.Event pfc_ReplaceDLG()
End If
19.6.3 DropDown Search服务
数据窗口对象中使用下拉数据窗口编辑风格的字段,可以方便用户录入数据,并能在一定程度上保证录入数据的规范。但是,如果下拉框中包含的数据过多(3页以上时),使用这种录入方式有时还不如直接录入快捷。如果能够在用户输入前面的字母时下拉框能够自动显示与之匹配的选项,无疑可以加快录入的速度。PFC中提供DropDown Search服务,可以让用户在下拉框中键入字母时自动进行匹配查找。缺省时,下拉框允许用户键入一个字母,PowerBuilder使用户移动到所键入字母开始的第一行。遗憾的是,用户无法键入多个字母来查找更适合的项目。
首先应该使用of_SetDropDownSearch(True) 启动该服务。然后,指定想让数据窗口中哪些栏具有搜索功能,并使用下面函数打开此功能:
dwcontrol.instancename.of_AddColumn ( { column } )
其中,dwcontrol为基于u_dw用户对象的数据窗口对象;instancename为n_cst_dwsrv_dropdownsearch实例变量,缺省为inv_dropdownsearch;可选参数column是String类型的,用来指定要添加到istr_columns数组中的列,该列应该是下拉数据窗口的编辑样式,并且包含的数据类型为String类型,如果省略该参数则添加数据窗口对象中所有的具有下拉数据窗口编辑样式的字段。
最后一个步骤是将数据窗口控件的EditChanged和ItemFocusChanged事件重定向到其对应的PFC事件pfc_EditChanged和pfc_ItemFocusChanged。例如,在EditChanged事件中可以编写如下脚本来重定向事件:
inv_dropdownsearch.Event pfc_EditChanged(row,dwo,data)
19.6.4 Required Column服务
为了保证用户在非空字段中必须输入数据,原来常用的方法是将字段的Required属性设置为True。但是,这样做经常在没有输入数据的字段失去焦点时显示提示窗口,这容易导致用户为了绕过该信息而随便输入数据。为了解决这个问题,还可以编写脚本在数据提交之前检验是否在所有的必须输入字段中输入了数据。Required Column服务为开发人员解决了该问题。
为了使用该服务,首先使用of_SetReqColumn(True)来启动该服务。选中数据窗口对象中字段的Required属性(使该属性的取值为True),当窗口激活pfc_Save事件时,PFC自动进行检查以确保Required字段都有值。
该服务还提供了几个函数以便编程灵活控制。比如,当需要使用PowerBuilder本身的处理机制来校验一些Required字段时,可以调用of_RegisterSkipColumn 函数,该函数的语法如下:
dwcontrol.instancename.of_RegisterSkipColumn ( column )
其中,dwcontrol是基于u_dw 的数据窗口控件名称;instancename是n_cst_dwsrv_reqcolumn 实例变量名称,缺省是inv_reqcolumn ;column是String类型的参数,是数据窗口中字段的名称。例如:
dw_emplist.inv_reqcolumn.of_RegisterSkipColumn ("dept_id")
19.6.5 Linkage服务
数据窗口的Linkage服务对于协调两个或者多个数据窗口之间的处理是极其方便的。Linkage服务最常见的服务是提供一个主/从关系的两个数据窗口。下面逐步介绍使用该服务的过程。
a. 调用of_SetLinkage(True)启动服务。例如:
dw_order_header.of_SetLinkage(True)
dw_order_detail.of_SetLinkage(True)
b. 调用of_SetMaster建立主从数据窗口之间的联系。例如:
dw_order_detail.inv_linkage.of_SetMaster(dw_order_header)
主从数据窗口之间的联系是通过它们的字段之间的联系来实现的。
c. 调用of_SetTransObject为主从数据窗口设置事务对象。例如:
dw_order_header.of_SetTransObject(SQLCA)
d. 调用of_Register设置主从数据窗口之间相联系的字段。例如:
dw_order_detail.inv_linkage.of_Register ("header_emp_id","detail_emp_id")
e. 设置主从数据窗口数据修改的顺序。例如:
dw_order_detail.inv_linkage.of_SetUpdateStyle(dw_order_detail.inv_linkage.BOTTOMUP)
当不设置数据的修改顺序时,则使用先主后从的缺省修改方法。
f. 调用of_SetStyle来设置当主数据窗口中数据发生改变时如何改变从数据窗口中的数据。例如:
dw_order_detail.inv_linkage.of_SetStyle (dw_order_detail.inv_linkage.RETRIEVE)
该语句指定当主数据窗口中的数据改变时检索从数据窗口中的数据。函数of_SetStyle可以设置主数据窗口数据行变化时从数据窗口的动作。该函数的参数可用的取值及其含义如下:
1或者FILTER 过滤从数据窗口;
2 或者RETRIEVE 检索从数据窗口;
3 或者SCROLL 滚动从数据窗口。
前面6个步骤的设置工作可以在一个事件中完成,比如典型情况是在窗口的Open事件中完成。
g. 前面几个步骤完成了必须的设定,下面就应该检索数据了。调用of_Retrieve函数检索数据。例如:
If dw_order_header.of_Retrieve( ) = -1 Then
MessageBox("错误","数据检索错误!")
Else
dw_order_header.SetFocus( )
End If
h. 在主数据窗口的pfc_Retrieve事件中编写脚本检索数据:
Return This.Retrieve( )
下面介绍一个完整的实例。假设两个数据窗口建立主从关系,dw_order_header为主数据窗口,dw_order-detail为从数据窗口。在窗口的Open事件中编写如下脚本:
dw_order_header.of_SetLinkage(True)
dw_order_detail.of _SetLinkage(True)
dw_order_header.of_SetTransObject(SQLCA)
dw_order_detail.inv_linkage.of_SetMaster(dw_order_header)
dw_order_detail.inv_linkage.of_Register ("header_emp_id","detail_emp_id")
dw_order_detail.inv_linkage.of_SetStyle(inv_linkage.SCROLL)
dw_order_detail.inv_linkage.of_SetStyle (dw_order_detail.inv_linkage.RETRIEVE)
dw_order_header.of_Retrieve()
19.6.6 MultiTable Update服务
该服务为用户提供了在一个数据窗口中修改多个数据表的功能。在过去的开发中,一个数据窗口中要修改多个数据表时,需要使用语句逐个修改数据窗口对象的相关属性,然后逐个修改数据表,这样编写脚本的工作量比较大。PFC为开发者提供了一种易于使用的方法,让开发者以较少的脚本就可以实现在一个数据窗口中修改多个数据表的功能。当数据窗口中包含来自多个数据表的字段,并且这些字段都有可能要修改数据表,这时可以考虑使用该服务。
首先应该调用of_SetMultiTable(True)来启动该服务,该服务不像其他大多数的服务,它可以在数据窗口注销时自动注销。接下来调用of_Register函数注册每个数据表修改时的信息,最后在数据保存时调用pfc_Save事件,该事件可以逐个保存数据窗口中不同数据表的数据。
函数of_Register是该服务中一个非常重要的函数,它的语法格式是:
dwcontrol.instancename.of_Register ( table, keycolumns
{ updatablecolumns {, keyinplace, whereoption } } )
其中,dwcontrol为基于u_dw的数据窗口控件名称;instancename为n_cst_dwsrv_multitable类型的实例变量,缺省为inv_multitable;table为要修改的数据表的名称,为String类型参数;keycolumns为String类型的数组,为要修改的数据表的关键字段(Key Column);updatablecolumns为String类型的数组,用来指定可以修改的字段,缺省情况下为table参数指定的表中所有字段;keyinplace为Boolean类型参数,用来表示是先删除行然后再插入(False)还是在主关键字发生变化时就在原位进行更新(True),该参数缺省取值为False;当指定了参数keyinplace时必须也设定参数whereoption,该参数为整型,表示如何创建UPDATE和DELETE语句的WHERE子句(0表示关键字栏,1表示关键字和可更新栏,2表示关键字和已更新栏)。后三个参数为可选参数,如果不指定这些可选参数则使用缺省取值。
下面是一段例程为数据窗口中注册可修改表及其关键字段:
String ls_projcols[ ] = {"proj_id"}
String ls_taskcols[ ] = {"proj_id", "task_id"}
dw_project.inv_multitable.of_Register("project", ls_projcols)
dw_project.inv_multitable.of_Register("task", ls_taskcols)
该脚本注册数据窗口dw_project中以proj_id字段为关键字修改数据表project,以proj_id和task_id为关键字修改数据表task。
19.6.7 Row Management 服务
对数据窗口中的数据行经常要进行删除、插入或恢复删除等操作。Row Management服务可以提供这些功能。PFC通过n_cst_dwsrv_rowmanager用户对象来进行行管理。该服务提供的功能具体有:
l 在数据窗口最后一行添加一个空记录;
l 在已有的数据行之间插入一个空记录;
l 删除一行或者多行数据;
l 显示一个对话框,允许用户选择那些已删除的数据行恢复。
首先调用of_SetRowManager(True)启动该服务,接下来根据不同的操作调用相应的函数即可。下面对这些功能分别举例说明。
(1)在数据窗口最后一行添加一个空记录
只需要调用pfc_AddRow事件即可。例程如下:
Long ll_return
ll_return = dw_emplist.inv_rowmanager.Event pfc_AddRow()
If ll_return = -1 Then
MessageBox("错误", "不能正常添加空记录!")
End If
(2)在已有的数据行之间插入一个空记录
只需要调用pfc_InsertRow事件即可。例程如下:
Long ll_return
ll_return = dw_emplist.inv_rowmanager.Event pfc_InsertRow()
If ll_return = -1 Then
MessageBox("Error", "Insert error")
End If
(3)删除一行或者多行数据
只需要调用pfc_DeleteRow事件即可。例程如下:
Long ll_return
ll_return = dw_emplist.inv_rowmanager.pfc_DeleteRow()
If ll_return = -1 Then
MessageBox("Error", "Deletion error")
End If
(4)恢复已删除数据行
只需要调用pfc_ RestoreRow事件即可。该事件另外打开一个窗口,将最近一次提交后删除的所有记录列出,用户可以指定将哪些数据恢复。如果没有删除数据,则直接提示没有删除数据而不打开该窗口。例如:
dw_1.inv_rowmanager.Event pfc_RestoreRow()
19.7 可视化控件
PFC中的可视化控件包括标准可视用户对象和定制可视用户对象两种。标准可视用户对象扩充了PowerBuilder的控件,一般由一个标准控件扩充,为其提供一些附加的逻辑,增强了控件的可重用性和函数功能。使用PFC提供的TreeView控件u_lb可以充分体现功能的强大。定制可视用户对象一般是由几个控件组成一个单元,并增加相应的逻辑结构来完成一定的处理功能。日历控件u_calculator是一个实例。
对于标准可视控件,本节介绍它的基本函数,作为一个实例介绍数据窗口控件u_dw。其他内容可以参见相关的PowerBuilder本身提供的Online Books。
19.7.1 标准可视用户对象的基本函数
标准可视用户对象继承自PowerBuilder的标准控件,并进行了适当的扩充。当然,开发人员也可以再做适当的扩充,以使之更适合自己的商业规则。下面分别介绍标准可视用户对象的基本函数。
1.剪切、拷贝和粘贴
编辑性的控件包括一些基本的编辑函数和其他相关的编辑控制函数。表19-15列出了和PowerBuilder中的控件相对应的PFC控件。
表19-15 控件对照
PB控件 | PFC控件 | PB控件 | PFC控件 |
DropDownListBox | u_ddlb | DropDownPictureListBox | u_ddplb |
DataWindow | u_dw | EditMask | u_em |
MultiLineEdit | u_mle | OLE Custom control | u_oc |
RichTextEdit | u_rte | SingleLineEdit | u_sle |
PFC通过为PB的标准控件定义事件并提供相应的脚本来提供这些编辑功能。例如,Clear功能是提供pfc_Clear事件及有关的脚本来完成的,开发人员需要完成此功能时只需要调用该事件即可。功能和事件对照如表19-16所示。
表19-16 功能和事件对应关系
功 能 | 事 件 | 功 能 | 事 件 |
Clear | pfc_Clear | Copy | pfc_Copy |
Cut | pfc_Cut | Paste | pfc_Paste |
Select All | pfc_Select All | Undo | pfc_Undo |
Paste Special | pfc_PasteSpecial |
|
|
当在窗口中使用这些PFC控件并且窗口菜单也是继承自m_master时,这些编辑功能自动起作用。m_master菜单中包括一个含有所有编辑功能的下拉菜单,只要在控件中通过消息路由发送相应的消息即可调用相关功能。
2.弹出菜单
编辑性的控件提供RbuttonUp事件,在用户点击鼠标右键时弹出菜单,提供一些编辑命令的快捷使用方式。不同的控件提供不同的右键弹出菜单,如表19-17所示。
表19-17 弹出菜单
菜 单 | 控 件 |
m_edit | u_ddlb,u_ddplb,u_em,u_mle,u_rte,u_sle |
m_dw | u_dw |
m_lvs | u_lvs |
m_oc | u_oc |
m_tvs | u_tvs |
这些菜单都提供基本的文本编辑功能。但是缺省情况下,并不是所有菜单中的这些基本编辑功能都可用,有些菜单项还是不可见的。这些控件都提供了pfc_PreRMBMenu事件,该事件在菜单创建之后显示之前触发,可以在该事件中编写脚本来定制菜单的显示。比如,取消m_dw菜单中的插入、删除菜单项可以在该事件中编写如下脚本:
am_dw.m_table.m_insert.Enabled = False
am_dw.m_table.m_delete.Enabled = False
其中,am_dw为pfc_PreRMBMenu事件的参数,该函数是通过引用方式传递的。还可以禁用右键弹出菜单,尤其在控件中的内容为只读时。只需要在有弹出菜单的控件中编写脚本即可,典型是在Constructor事件中编写如下脚本:
This.ib_rmbmenu = False
对于数据窗口的菜单m_dw可以省略该脚本,因为当数据窗口为只读时PFC将自动禁用它的右键弹出菜单。
另外,还可以在menu画板中对上面的菜单进行扩充,添加自己的菜单项。
3.自动滚动
DropDownListBox和DropDownPictureListBox两个控件提供用户键入字母时自动滚动到匹配的选项上。这个功能有点类似于下拉数据窗口中的DropDown and Search服务。比如,当在这两个控件中键入g时自动滚动到以g开头的第一个选项,继续键入b则滚动到第一个以gb开头的选项。
缺省情况下,这两个控件的自动滚动功能不可用。典型编程事件是Constructor,编写如下脚本即可使该功能可用:
This.ib_search = True
4.自动选中
编辑性的控件获得焦点时,自动选中其中的文字内容。提供该功能的可编辑性控件有u_ddlb,u_ddplb,u_em,u_mle和u_sle。缺省情况下,这些控件的自动选中功能不可用。典型编程事件是Constructor,编写如下脚本即可使该功能可用:
This.ib_autoselect = True
5.显示微帮助
大多数控件都在GetFocus事件中提供了脚本,在相应的Frame窗口中显示Tag值作为微帮助。当控件获得焦点时,触发GetFocus事件,该事件中提供了已经编写好的脚本来触发控件所在窗口的pfc_ControlGotFocus用户事件。pfc_ControlGotFocus事件将控件的Tag值作为微帮助显示在状态栏中。
开发人员要使用该功能,只需要在适当的事件中编写脚本。首先应该允许显示微帮助,使用如下脚本:
gnv_app.of_SetMicroHelp(True)
然后将控件的Tag值作为微帮助,使用下面脚本:
MicroHelp=tagtext
6.反显选中项目
ListBox和PictureListBox提供反显项目的功能。当调用这两个控件的pfc_InvertSelect事件时,已经高亮显示的项目取消高亮显示,没有高亮显示的项目高亮显示。
在菜单或者窗口的这两种控件中都可以编写脚本使用该功能。在菜单中只要编写如下脚本即可:
of_SendMessage("pfc_InvertSelection")
在窗口的控件中,可以编写如下脚本:
lb_choices.Event pfc_InvertSelection() //在控件lb_choices的适当事件中
19.7.2 数据窗口控件u_dw
PFC提供了高级标准可视控件,这些控件的使用可以更多地提高开发效率,开发出功能更为强大的应用程序。这些高级控件以及它们和PowerBuilder标准控件的对应关系如表19-18所示。
表19-18 控件对照
PB控件 | PFC控件 | PB控件 | PFC控件 |
DataWindow | u_dw | ListView | u_lvs |
TreeView | u_tvs | RichTextEdit | u_rte |
OleControl | u_oc | Tab | u_tab |
Tab page | u_Tabpg |
|
|
其中,数据窗口控件u_dw是一个非常重要的控件。在前面一节中已经对数据窗口服务做过了详细的介绍,这里只对该控件的其他内容做详细介绍。其他控件可以参见PowerBuilder自带的Online Books中的相关章节。
u_dw控件继承自PB的数据窗口,提供了一些内置的扩展函数或者事件。说明如下。
(1)启动或者停止数据窗口服务的函数
PFC为数据窗口提供的服务以及如何停、启这些服务在前面一节都做过了详细的介绍,此处不再赘述。
(2)设置事务对象的函数
当事务对象为n_tr类型时可以为u_dw设置事务对象。使用函数of_SetTransObject可以设置事务对象,以后可以通过实例变量itr_object来引用该事务对象。
对于使用了Linkage服务的主从数据窗口,只有当主从关系都建立好后才能设置事务对象,只需要调用n_cst_dwsrv_linkage的of_SetTransObject函数为主窗口设置即可。这些都在前面介绍数据窗口的服务时做过了详细介绍。
(3)为数据窗口或者下拉数据窗口检索数据的事件
当检索基于u_dw的数据窗口中的数据时,应该调用of_Retrieve函数。该函数根据具体情况或者调用数据窗口的pfc_Retrieve事件,或者调用n_cst_dwsrv_linkage的of_Retrieve函数,这取决于是否启动了数据窗口的Linkage服务。在pfc_Retrieve事件中应该编写脚本来检索数据。比如,在数据窗口的Constructor事件中编写如下脚本:
Long ll_return
ll_return = This.of_Retrieve()
在数据窗口的pfc_Retrieve事件中编写如下脚本:
Return This.Retrieve()
当使用Linkage服务建立了主从数据窗口时,如果主从联结方式为Retrieve,则只需要在主数据窗口的pfc_Retrieve事件中编写和上面相同的脚本;主从数据窗口使用其他联结方式时,则应该在每一个从数据窗口的pfc_Retrieve事件中也编写和上面相同的脚本。
需要检索下拉数据窗口中的数据时,在数据窗口的pfc_PopulateDDDW事件中编写脚本。该事件在弹出下拉数据窗口时触发。例如,脚本如下:
If as_colname = "dept_id" Then //使用事件参数来判断是否当前列为dept_id
adwc_obj.SetTransObject(SQLCA) //使用事件参数为数据窗口设置事务对象
Return adwc_obj.Retrieve() //返回检索到的数据
Else
Return 0 //当前是其他列,则直接返回
End If
(4)控制数据窗口数据修改的事件
PFC提供了两种修改控制数据的方式,一种是通过u_dw的pfc_Update事件;一种是通过w_master窗口的pfc_Save事件。前一种方式中,通过调用相关事件完成数据修改,不需要Logic Unit服务,并且当数据窗口的MultiTable服务启动时自动调用n_cst_dwsrv_multitable的of_Update来修改各个数据表,这种方式需要数据窗口都是基于u_dw的。后面这种修改方式需要Logic Unit服务(可以自动启动该服务),自动调用相关函数修改窗口上的所有数据窗口中的数据,对于非基于u_dw的数据窗口自动调用PowerScript函数来修改。使用这种修改方式要求窗口都是继承自PFC的w_master,因为在该窗口的子窗口中事件pfc_Save都可用。
当只修改窗口中的一个数据窗口中的数据时可以使用如下脚本:
If dw_emplist.Event pfc_Update (True, True) = 1 Then //如果修改成功
SQLCA.of_Commit() //提交
Else //否则
SQLCA.of_Rollback() //回退
End If
当修改窗口上的多个数据窗口中的数据时,可以调用如下脚本:
Integer li_return
li_return = w_emp.Event pfc_Save() //调用pfc_Save事件
If li_return < 0 Then
MessageBox("错误提示", "修改失败,错误代码为"+String(li_return))
Else
gnv_app.of_GetFrame().SetMicroHelp ("修改成功")
End If
当窗口中包含多个数据窗口,多数允许保存,少数不允许保存,这时可以使用pfc_Save事件保存,使用函数of_SetUpdateable将少数数据窗口定义为不可修改的。例如:
dw_emplist.of_SetUpdateable(False)
当数据窗口中的数据发生了修改,并且窗口关闭之前用户没有保存数据,系统将触发CloseQuery事件,该事件调用pfc_Save事件,显示询问窗口询问用户是否保存数据,根据用户的选择决定是否保存。
(5)控制打印的事件
u_dw控件允许用户以三种方式进行打印前的设置。第一种,显示一个打印对话框,用户可以在该窗口中指定打印参数。要使用这种方式,只需要调用pfc_Print事件即可,比如下面设定数据窗口dw_emp以这种方式进行设置:
dw_emp.Event pfc_Print()
用户指定的打印参数都保存在PFC提供的结构s_printdlgattrib中,可以在pfc_PrePrintDlg事件中修改该结构成员变量的取值来进一步定制打印参数。比如,可以在该事件中编写如下脚本:
astr_printdlg.l_copies = 2 //定义打印2份
第二种方式是直接打印,不显示任何信息。要使用这种方式,只需要调用pfc_PrintImmediate事件即可。比如下面事件触发时立即打印数据窗口dw_emp中的内容:
dw_emp.Event pfc_PrintImmediate()
第三种方式是显示一个打印纸设置对话框,用户可以指定打印纸参数。使用这种方式只需要调用数据窗口的pfc_PageSetup()事件即可。比如,下面的语句可以让用户在打印之前设定纸张参数:
dw_emp.Event pfc_PageSetup()
在显示该配置对话框时可以触发pfc_PrePageSetupDlg事件,可以在该事件中提供一些缺省的或者建议性的参数设置。比如,在事件中编写下面的脚本可以在纸张设置对话框打开时设定缺省的打印方式:
astr_pagesetup.b_portraitorientation = True
(6)添加服务
明白PFC中的服务和服务所依附的对象并且了解PFC的体系结构后,就可以对某种对象增加新的服务形式来提供增强功能。比如,需要为数据窗口提供一个新的服务时,可以从基类对象n_cst_dwsrv继承。在u_dw类中添加一个指向该服务的实例引用变量,在u_dw类中声明一个名称类似于of_SetXXX的对象函数来停、启该服务,并编写相应的代码即可。