数据库快速开发工具 PowerBuilder 从5.0版本开始提供了一套基础类库PFC(PowerBuilder Foundation Class),使得应用程序的开发速度又提高了一大步。由于PFC使用的不是很广泛,许多开发者对此还不是很了解。下面就我使用的过程中所得到的心得与遇到的问题,整理出来与大家共享与探讨。各位觉得有不合适的地方,希望批评指正。
PFC的帮助对各个独立服务、事件或者函数有一些实例代码,但是对于整体的使用几乎没有很全面地介绍。因此,我写这些东西,作为PFC帮助的一个辅助材料,大部分是在PFC帮助上没有的内容,是我使用PFC的过程中的总结与归纳。
PFC入门
首先,介绍一下如何创建一个简单的PFC应用程序。
构造一个简单的PFC程序框架
使用PFC,我们得先学会使用PFC构造一个简单的应用程序。我们可以根据PFC帮助提供的模式来构造我们的应用程序框架(参见PFC帮助的Overview 中的 For a quick start项),也可以查看PFC例程(Peat、Example等)仿造构造我们的应用程序。下面简单的陈述一下我通常使用PFC构造应用程序的步骤:
首先,创建一个空的应用程序,从对象n_cst_appmanager继承一个新的用户对象,命名为n_mdl_app;
其次,根据PFC帮助快速模型实例创建简单的应用程序框架,有所不同的是:应用程序服务,我使用刚才创建的用户对象n_mdl_app代替了n_cst_appmanager,
设置n_mdl_app的相关事件的代码,可参见PFC例程的程序代码;
打开应用(Application)对象,更改部分属性及代码:
在Additional Properties中,更改Variable Types 页中的3个变量(SQLCA->n_tr; Error->n_err; Message->n_msg)(参考PFC例程Peat)
根据需要更改默认字体的类型及大小(由于我们开发的都是中文界面的应用程序,所有字体更改为“宋体 9pt”),根据需要更改应用程序的图标;
根据需要更改其他的属性,比如:DisplayName, MicroHelpDefault, 等等;
另外,我还需要更改应用对象的SystemError事件的代码,我使用的是逄新利编写的一个错误陷阱模块。
根据需要编写.Ini文件或者更改注册表项值。
好了,到此为止,一个PFC应用程序框架就完成了。试着运行一下,可以看到框架窗口的运行状态。
添加一个工作区窗口
应用程序框架窗口实现出来了,为了能够工作,我们还得有工作区窗口(Sheet)。
首相,随便创建一个Grid型的数据窗口对象,保存为d_demo1。然后从w_sheet窗口继承出来一个窗口对象,然后在这个窗口中放置一个用户对象u_dw(数据窗口的基础祖先对象)。该数据窗口控件的数据对象为刚才创建的数据窗口对象d_demo1。
在窗口的Open事件中编写如下代码:
// 开启Resize服务
this.of_SetResize(True)
//设置Resize服务的原始大小
this.inv_Resize.of_SetOrigsize( dw_Demo.Width + dw_Demo.X * 2, dw_Demo.Height + dw_Demo.Y * 2 )
//注册要实施Resize服务的控件及方法
this.inv_Resize.of_Register(dw_Demo,this.inv_Resize.SCALERIGHTBOTTOM)
//异步触发事件,利用消息路由发送消息。该消息是要检索数据
this.Event Post pfc_MessageRouter('pfc_Retrieve')
在数据窗口控件的Constructor事件中编写如下代码:
this.of_SetTransObject(SQLCA)// 设置事物对象
this.of_SetBase(True)// 开启数据窗口基本服务
this.of_SetPrintPreView(True)// 打印预览服务
this.of_SetRowManager(True)// 行管理服务
this.of_SetRowSelect(True)// 行选择服务
this.of_SetFind(True)// 查找服务
this.of_SetSort(True)// 排序服务
this.of_SetFilter(True)// 筛选服务
this.inv_RowSelect.of_SetStyle(this.inv_RowSelect.EXTENDED)// 设置行选择服务为高级选择模式,支持Ctrl & Shift键
this.inv_Sort.of_SetStyle(this.inv_Sort.DROPDOWNLISTBOX)// 设置排序服务的对话框样式
this.inv_Filter.of_SetStyle(this.inv_Filter.SIMPLE)// 设置筛选服务的对话框样式
this.inv_Sort.of_SetColumnDisplayNameStyle(this.inv_Sort.HEADER)// 设置排序服务的显示内容
this.inv_Filter.of_SetColumnDisplayNameStyle(this.inv_Filter.HEADER)// 设置筛选服务的显示内容
在数据窗口控件的pfc_Retrieve事件中编写如下代码:
Return this.Retrieve()
如果该窗口需要保存数据,在窗口的pfc_EndTrans事件中编写如下代码:
Integer li_TransRtn
If ai_Update_Results = 1 Then
// 如果更新数据库成功, 则提交事务
li_TransRtn = SQLCA.Of_Commit()
Else
// 否则, 回滚事务,并提示信息
li_TransRtn = SQLCA.Of_Rollback()
of_Messagebox('Update DB Failed','提示','数据保存失败。',Exclamation!,OK!,1)
End If
// 返回该事件发生的情况 SUCCESS & FAILURE 是w_master的实例变量
If li_TransRtn = 0 Then
Return SUCCESS
Else
Return FAILURE
End If
如果该窗口的数据不需要保存,则要将数据窗口控件的ib_UpdateAble属性选择为False,这样在更改了数据以后,关闭窗口是不会提示类似于“数据以更改,是否保存”等的信息了。
将该窗口命名保存为w_Demo1。
在应用程序的主菜单上添加一个项目,编写该项目的Clicked事件代码为:
Message.StringParm = 'w_demo'
of_SendMessage('pfc_open')
好了,运行应用程序,看一下运行的效果吧。你可以查看以下的效果:
使用鼠标右键单击数据窗口,查看数据窗口的右键菜单;
删除一条数据,然后右键单击数据窗口,选择“Restore”项,看一看恢复删除的功能;
点击窗口菜单上的“Sort”、“Filter”、“Print Preview”,“Find”,“Replace”等菜单项,查看数据窗口的“排序”、“筛选”、“打印预览”,“查找”,“替换”等的功能;
点击菜单[View]的[First/Prior/Next/Last]等菜单项,查看数据窗口的翻页功能;
修改部分数据,关闭窗口,查看提示保存的对话框
以上的这些功能,使用PFC的时候,简单到只需设置某项属性或者编写一句开启某项服务的语句即可实现。如果你想对某项服务提出更高的要求,比如排序服务,你想更改服务的模式由默认的PB拖放排序方式改为下拉列表框方式,还需要一行命令来更改服务的方式。
完善并增加更多的功能
如果您想增加更多的更强大的功能,比如闪屏,登录窗口等等。下面就简单的介绍一下增加这些功能的简单的方法。顺便介绍一下我们可也在这些事件中添加的代码和开启的服务。涉及到的有关的具体问题会在以后慢慢的了解到。
通过查看PFC例程Peat的代码,我们知道可以在“应用程序管理器”对象(gnv_App的对象)的Contructor事件中初始化版本、公司、INI文件等的信息(实例变量值,这些值大多可以在属性窗口中设置),可以在“应用程序管理器”对象的pfc_Open事件中开启需要的应用程序服务,这些服务包括:应用程序选项服务、数据窗口缓存服务、错误信息服务、调试服务、安全服务、事务注册服务、最近使用对象服务等等。
其次,我们可以在该对象的pfc_PreAbout,pfc_PreSplash,pfc_PreLogonDlg事件中添加代码来定制“关于”、“登录”以及闪屏对话框的显示。在这三个事件中各有一个参考型的参数,这些参数是相应的属性对象实例,开发人员可以改变这些对象的属性,来达到控制对话框显示信息目的。如果不更改,将按照默认的样式现实。
如果需要,“应用程序管理器”对象的pfc_Idle,pfc_ConnectionBegin、pfc_ConnectionEnd事件中编写代码,并在应用的相应事件中触发这些事件。
如果需要显示登录窗口,需要完成以下步骤:
在框架窗口的Open事件中调用of_LogonDlg()函数:
Integerli_Rtn
//调用函数打开登录对话框
li_Rtn= gnv_App.of_LogonDlg()
If li_Rtn = 1 Then this.SetMicroHelp('Logon Successful')
Else If li_Rtn = -1 Then
MessageBox('Logon','Logon Failure' + String(li_Rtn))
End If
//Close(this)
Halt Close
End If
在“应用程序管理器”对象的pfc_Logon事件中添加代码来处理用户的登录。例如可以是如下的代码:
Integerli_Rtn
Stringls_IniFile
ls_IniFile= this.of_GetAppIniFile()
If SQLCA.of_Init(ls_IniFile,"DataBase") = -1 Then
li_Rtn= FAILURE
End If
//as_UserID & as_Password为传递给事件的参数
SQLCA.of_SetUser(as_UserID,as_Password)
If SQLCA.of_Connect() = -1 Then
li_Rtn= FAILURE
Else
gnv_App.of_SetUserID(as_UserID)
li_Rtn= SUCCESS
End If
Returnli_Rtn
其实,在我们大多开发的应用程序中,登录窗口检验的是我们存放在数据库中的用户列表,而不是登录数据库的用户身份验证。开发人员可根据需要自行更改。
另外,我们可以开启框架窗口的状态条服务和工作区窗口(Sheet)管理服务。要开启这两个服务,我们只需在框架窗口的Open事件中编写相应的代码。不过,PFC的状态条实在是差强人意。
消息路由器
CSDN上曾有网友提问要知道PFC消息路由器的机制问题,下面我从《PowerBuilder 基础类库技术详解》一书摘抄一节介绍消息路由器的部分。
PFC使用消息路由器来处理菜单和窗口之间的通信。这种定制的消息传送机制内至于所有PFC菜单和窗口中。
虽然消息路由器可以用于任何对象与一个窗口的通信,但是他通常用来将消息从菜单传递给窗口。消息路由器根据一种定制的搜索算法来决定接受该消息的对象。
使用消息路由器有以下优点:
菜单的脚本只需要知道要调用的用户事件,而不必知道当前窗口或相关控件的名称;
窗口不必保留那些只是简单地调用数据窗口的用户事件。这样,窗口所保留的用户事件的数目就减少了。
消息路由器函数传递的消息实际上就是一个字符串,该字符串包含窗口或窗口的控件要激活的用户事件的名称。消息路由器包括内置的调试信息,从而可以提供错误信息。
当用户选中一个菜单项时,该菜单项的Clicked事件调用该菜单的of_SendMessage函数,并传递要调用的用户事件的名称。 of_SendMessage函数调用n_cst_menu的of_SendMessage函数,该函数调用窗口的pfc_MessageRouter事件,该事件再调用制定的用户事件。
根据应用程序是MDI程序还是SDI程序,of_SendMessage调用的pfc_MessageRouter用户事件的方式有所不同,如图:
pfc_MessageRouter用户事件调用窗口、当前控件以及最近即获得数据窗口中传递的用户事件,如图:
消息路由器提供一种菜单和窗口之间的通信机制。除了数据窗口中的命令按钮之外,没有按钮能够调用pfc_MwssageRouter事件。这是因为详细函数调用GetFocus事件来的到当前的控件,而实际上点中一个命令按钮之后,当前的控件就是该命令按钮本身。
以上内容摘自《PowerBuilder 基础类库技术详解》,电子工业出版社出版,王梅君 等编著。
该书是 刘红岩 主编的《PowerBuilder 7.0 与 Sybase Internet 技术丛书》系列中的一员。