Pow erBuilder9.0 基础应用与系统开发

471
P o w erB u ild er 9 基础应用与系统开发 崔杜武 姚全珠 黑新宏 周红芳 等编著

Transcript of Pow erBuilder9.0 基础应用与系统开发

Pow erB uilder 9.0

基础应用与系统开发

崔杜武 姚全珠 黑新宏 周红芳 等编著

内 容 提 要

本书全面介绍了 PowerBuilder 9.0 的基本使用方法和新功能,本书重点讲解了 PowerBuilder 9.0 中新增

加的强大的 Web 开发功能,包括 JSP 站点开发、Web 服务、XML 数据窗口、第三方应用服务器支持、

PowerBuilder Native Interface (PBNI)、PDF 报表生成以及源代码管理等功能。根据作者多年开发

PowerBuilder 实际项目的经验,从开发应用软件的角度出发,对 PowerBuilder 9.0 的数据库工具、关键控

件、数据窗口,以及 PowerScript 等重要的基础知识进行了详细的讲解,本书的后半部分给出了多个应用

系统的开发实例,帮助读者通过实例快速掌握实际开发技能。

本书部分章节设计了一定的练习题,帮助读者掌握相关知识重点,同时也为选用本书作为教学用书的

老师提供方便。

本书附赠 Sybase Inc 赛贝斯软件(中国)有限公司授权之 PowerBuilder 9.0(评估版)软件光盘,供读者学

习使用。

未经许可,不得以任何方式复制或抄袭本书之部分或全部内容。

版权所有,侵权必究。

图书在版编目(CIP)数据

PowerBuilder 9.0 基础应用与系统开发/崔杜武等编著.—北京:电子工业出版社,2004.4 ISBN 7-5053-9699-4

Ⅰ.P... Ⅱ.崔... Ⅲ.数据库系统-软件工具,PowerBuilder 9.0 Ⅳ.TP311.56

中国版本图书馆 CIP 数据核字(2004)第 013947 号

责任编辑: 张瑞喜

印 刷: 北京市天竺颖华印刷厂

出版发行: 电子工业出版社出版 http://www.phei.com.cn

北京市海淀区万寿路 173 信箱 邮编 100036

经 销: 各地新华书店�

开 本: 787×1092 1/16 印张:29.5 字数:660 千字

印 次: 2004 年 4 月第 1 次印刷

印 数: 5000 册 定价:55.00 元(附光盘 1 张)

凡购买电子工业出版社的图书,如有缺损问题,请向购买书店调换。若书店售缺,请与本社发行部

联系。联系电话:(010)68279077。质量投诉请发邮件至 [email protected],盗版侵权举报请发邮件至

[email protected]

曹林博士 PowerBuilder 高级开发经理

Sybase 亚洲研发中心

PowerBuilder 以开放的数据库支持、强大的数据窗口技术、丰富的用户界面和极高的

开发效率作为优秀的第四代语言开发工具,已被广泛地应用于企业数据库应用系统开发。

据不完全统计,目前大约有 50 万个 PowerBuilder 企业应用程序在运行。

PowerBuilder 9.0 的发布更是一个大的飞跃。它不管是对 C/S 模式应用的开发,还是

B/S 模式的开发,以及 Web 应用的开发,在功能方面都有极大的增强。JSP Target 结合数据

窗口 Tag library(标签库)和 4GL 对象模型,为开发者提供了快速的和可视化的 Web 应用开

发工具。XML 数据窗口让开发者极为方便地将数据库处理结果存为 XML 格式、生成 PDF

格式的报表,或者将 XML 文件或流输入到数据窗口。PowerBuilder 的 DOM(PBDOM)功能

使开发者能用脚本语言轻松地处理 XML 数据。PowerBuilder Native Interface (PBNI)为开发

者提供了广阔的空间,用户可以通过 C++或 JAVA 进一步扩展 PowerBuilder 的功能。同时,

也可以将用户的 PowerBuilder 应用程序集成到 JAVA 或 C++应用系统中。PowerBuilder 9.0

引进了对第三方应用服务器的支持,用户可以用 PowerBuilder 快速地开发丰富的客户端应

用来访问 EJBs。PowerBuilder 9.0 还提供友好的界面让开发者方便地集成 Web Services。

PowerBuilder 9.0 奠定了新一代快速应用开发工具的基础。PowerBuilder 不仅是与时并

进,全方位地支持主要的开发平台 J2EE 和.NET,更重要的是对新的复杂的开发技术做出

了进一步抽象和简化,从而继续保持易于使用和高效率开发的优势。我们力求使应用开发

不局限于某些开发平台、架构和开发语言,从而保持应用的开放性和可扩展性。在将来的

新版本中,大家将很快就会看到更加令人振奋的发展。例如,用 PowerBuilder 开发的组件

既可以发布到主要的 J2EE 应用服务器上,也可以发布到.NET 平台上。进一步,用户可以

用 PowerBuilder 开发.NET 应用程序和 JAVA 应用程序。结合我们已经发布的 Pocket

PowerBuilder,用户不仅可以快速地开发新的无线应用,而且可以非常容易地将已有的

PowerBuilder 应用程序转换成无线应用。在下一个版本里,我们还将有机地集成我们的企

业建模和设计工具 PowerDesigner。PowerBuilder 用户可以在同一个集成开发环境中进行螺

旋渐进式的设计和开发,从而更进一步地提高了应用开发和维护的效率。

本人有机会拜读了崔杜武教授编写的《PowerBuilder 8.0 从基础到应用》。深感它是一

本很有价值的书,是作者多年开发经验的积累。崔教授和他的团队不仅对 PowerBuilder 开

发工具有深刻的研究,并且从 PowerBuilder 6.0 起就积累了丰富的 PowerBuilder 实际应用开

发经验。在和崔教授的交往中,我非常敬佩他培养人才和奉献社会的专业精神。崔教授曾

告诉我他的学生都有很好的就业机会。我们就期待崔教授能继续编写介绍 PowerBuilder 9.0

的书,让更多的读者受益。

《PowerBuilder 9.0 基础应用与系统开发》不仅完整而简洁地介绍了 PowerBuilder 9.0

的基本使用方法和新功能,而且根据作者多年开发 PowerBuilder 实际项目的经验,从开发

应用软件的角度出发,对 PowerBuilder 9.0 的数据库工具、关键控件、数据窗口,以及

PowerScript 等重要的基础知识进行了详细的讲解,并在每一部分都给出了实例。本书重点

讲解了 PowerBuilder 9.0 中新增加的强大的 Web 开发功能,包括 JSP 站点开发、Web 服务、

XML 数据窗口、第三方应用服务器支持、PowerBuilder Native Interface (PBNI)、PDF 报表

生成以及源代码管理等功能。

我深信,仔细阅读本书能让您开阔视野,丰富 PowerBuilder 开发经验,并大大提高开

发新一代应用系统的水平和效率。

2003 年 11 月于新加坡

前 言

2002 年,我们编著出版了《PowerBuilder 8.0 从基础到应用》一书,受到不少读者的欢

迎。有幸的是,PowerBuilder 软件的开发厂商 Sybase 公司新加坡分部曹林先生也看到了此

书,并给予了颇高的评价。在 PowerBuilder 9.0 发布之前,曹先生主动寄来 PowerBuilder 9.0

评估版光盘资料,并介绍了其他相关资料,希望我们继续编书介绍 PowerBuild 9.0。我们具

有多年应用 PowerBuilder 的工程经验,同时感到 PowerBuilder 是一个与时俱进的数据库开

发工具,最新发布的 PowerBuilder 9.0 版本为应用当前软件开发中的主要技术:JSP、Web

Service、XML、C++类、第三方应用服务(EJB、IBM WebSphere)等均提供了方便。因

而感到编一本 PowerBuilder 9.0 编程技术与开发实例方面的书籍是一件有意义的事。这一动

机得到了电子工业出版社张瑞喜女士的支持,因而才有今天摆在读者面前的此书。

本书从应用 PowerBuilder 开发软件的实际需要出发,力求给读者提供一本实用的技术

参考资料。全书共有 16 章。其中,第 1 章至第 8 章,以介绍 PowerBuilder 9.0 的基础为主;

第 9 章至第 12 章以介绍 PowerBuilder 9.0 的新技术为主;第 13 章至第 16 章介绍了运用

PowerBuilder 9.0 开发的 4 个应用系统的实例。

第 1 章简要介绍 PowerBuilder 9.0 的一些基本知识和新特性,使读者能够对

PowerBuilder 9.0 的安装方法、环境等基础知识有一定的了解。第 2 章至第 8 章结合开发实

际项目的经验,对 PowerBuilder 9.0 的数据库工具、PowerBuilder 9.0 数据库开发的关键控

件、PowerBuilder 9.0 集成的功能强大的数据窗口、数据管道、菜单,以及 PowerScript 脚

本语法等重要的基础知识进行了详细的讲解,并给出相应的例题。

第 9 章至第 12 章重点讲解 PowerBuilder 9.0 中新增加的强大的网络功能。其中第 9 章

主要介绍 JSP 的一些基础知识以及在 PowerBuilder 9.0 下作 JSP 开发的基本方法和步骤。第

10 章介绍使用 Web 服务的相关知识,主要描述了 Web 服务及其相关协议规范,并通过实

例详细介绍如何使用 PowerBuilder 9.0 和 EAServer 应用服务器创建 Web 服务及 Web 服务调

用客户端。第 11 章介绍关于 PowerBuilder 9.0 的新技术:PowerBuilder 本地接口�PBNI(PowerBuilder Native Interface),主要介绍 PBNI 的基本原理及其构成元素,并在此基础上

阐述 PowerBuilder 与 C++、JAVA 以及第三方组件之间的接口和消息传递原理及方法,并介

绍两个具体的 PBNI 应用实例。第 12 章介绍文档对象模型 PBDOM。它是 PowerBuilder 处

理工具,是通过 XML 文档可访问和操控的程序接口。它与 W3C DOM API 工具以及 Java基本文档对象模型 JDOM 很相似,PBDOM PowerBuilder API 可以从 PowerScript 源码中读

取、编辑并操作标准格式的 XML。PBDOM 把 XML 文档看作互连对象的集合,并提供直

观方式标识每个对象的用途和功能。 为了便于读者自学和检查对于 PowerBuilder 技术的掌握程度,以上各章后均留有一定

数目的练习题,这些题目都是经过作者精心设计的,这些练习所需要的知识点都在正文中

有详细讲解,只要读者能够认真学习和练习,就能顺利掌握 PowerBuilder 工具,进行工程

项目的开发。

第 13 章至第 16 章综合运用 PowerBuilder 9.0 编程技术,给出 4 个工程应用实例,它们

分别是:第 13 章——人事管理系统、第 14 章——学生成绩管理信息系统、第 15 章——网

上采购管理信息系统、第 16 章——仓储管理系统。对于每一系统,都给出了系统设计(含

目标设计、开发理念设计、开发及运行环境、功能分析及模块设计)、数据库设计(含概念

设计、逻辑设计、物理设计)及系统中大部分功能模块的实现界面和代码,所给代码都已

调试通过。我们期望这些介绍对读者进行工程应用开发会有所帮助。

本书附赠光盘是 PowerBuilder 9.0 的评估版软件,它是经曹林先生联系,征得 Sybase

公司授权,免费提供给读者学习使用的。在此我们向 Sybase 公司和曹林先生表示衷心的感

谢!

本书由西安理工大学计算机科学与工程学院崔杜武教授全面负责,并校阅审定了全部

书稿;姚全珠副教授组织编写并初审了第 13 章至第 16 章,参加该部分编写的还有博士生

张烨、硕士生张琳霞、黎永良、崔颖安、王馨梅、李伯宇、陈晨;黑新宏讲师组织编写并

初审了第 1 章至第 6 章,参加该部分编写的还有博士生王竹荣、硕士生侯浩录、黄军勤、

孙立石、李雪、任晓梅、何志兰、黄大鹏;周红芳博士生组织编写并初审了第 7 章至第 12

章,参加该部分编写的还有博士生王超学、硕士生陈皓、梁文静、闫炜、费蓉、江炎军、

杨德龙和仇建平。由于水平有限,书中难免存在不妥之处,恳请读者指正。

在本书的编写过程中,参考了部分相关资料及文献,在此向原作者们表示诚挚的谢意!

本书作者的电子信箱地址为:[email protected],欢迎与各位读者交流。

作 者

2004.3.2

·3·

目 录

第 1 章 PowerBuilder 9.0 基础和集成环境 ...................................................1

1.1 PowerBuilder 9.0 的基本概念.......................................................................................1

1.1.1 基本概念 .............................................................................................................1

1.1.2 PowerBuilder 9.0 的安装环境............................................................................1

1.1.3 PowerBuilder 9.0 的新特性................................................................................2

1.1.4 术语 .....................................................................................................................3

1.2 PowerBuilder 的环境介绍.............................................................................................4

1.2.1 整体界面 .............................................................................................................4

1.2.2 菜单栏 .................................................................................................................4

1.2.3 工具栏 .................................................................................................................6

1.2.4 Painter(画板)简介 ...............................................................................................8

1.3 练习题 ..........................................................................................................................12

第 2 章 使用 PowerBuilder 操纵数据库 ......................................................13

2.1 使用数据库画板建立数据库 ......................................................................................13

2.1.1 Database(数据库画板)......................................................................................13

2.1.2 创建 Sybase Server Anywhere 数据库.............................................................14

2.1.3 删除 Sybase Server Anywhere 数据库.............................................................15

2.2 PowerBuilder 与数据库的连接...................................................................................15

2.2.1 ODBC 接口.......................................................................................................15

2.2.2 专用接口 ...........................................................................................................16

2.2.3 数据库描述文件 ...............................................................................................17

2.2.4 ODBC 接口连接实例 .......................................................................................21

2.3 操作数据库表 ..............................................................................................................24

2.3.1 创建表 ...............................................................................................................24

2.3.2 修改表 ...............................................................................................................25

2.3.3 定义表的属性 ...................................................................................................26

2.3.4 主键 ...................................................................................................................27

2.3.5 定义外键 ...........................................................................................................27

2.3.6 删除表、主键、外键 .......................................................................................29

2.3.7 数据操作 ...........................................................................................................29

2.4 使用视图 ......................................................................................................................32

·4·

2.4.1 创建视图 ...........................................................................................................32

2.4.2 删除视图 ...........................................................................................................34

2.5 存储过程和触发器 ......................................................................................................34

2.5.1 存储过程 ...........................................................................................................34

2.5.2 触发器 ...............................................................................................................35

2.6 使用事务对象 ..............................................................................................................37

2.6.1 事务对象简介 ...................................................................................................37

2.6.2 SQLCA 全局事务对象 .....................................................................................38

2.6.3 自定义事务对象 ...............................................................................................39

2.7 创建和应用数据库的实例 ..........................................................................................40

2.7.1 创建数据库 .......................................................................................................40

2.7.2 应用数据库 .......................................................................................................40

2.8 练习题 ..........................................................................................................................42

第 3 章 PowerScript 语言 ........................................................................... 43

3.1 基础语法 ......................................................................................................................43

3.1.1 大小写与标识符 ...............................................................................................43

3.1.2 注释 ...................................................................................................................43

3.1.3 断行与续行 .......................................................................................................44

3.1.4 保留字 ...............................................................................................................44

3.1.5 操作符和优先级 ...............................................................................................45

3.2 数据类型 ......................................................................................................................47

3.2.1 标准数据类型 ...................................................................................................47

3.2.2 增强数据类型 ...................................................................................................48

3.2.3 对象型数据类型 ...............................................................................................49

3.2.4 枚举型数据类型 ...............................................................................................50

3.2.5 数据类型的转换 ...............................................................................................51

3.2.6 字符与字符串 ...................................................................................................51

3.2.7 数组 ...................................................................................................................53

3.3 变量与常量 ..................................................................................................................56

3.3.1 实例变量的访问控制 .......................................................................................56

3.3.2 变量的作用域 ...................................................................................................57

3.3.3 常量 ...................................................................................................................58

3.4 代词 ..............................................................................................................................58

3.4.1 This....................................................................................................................59

3.4.2 parent .................................................................................................................59

3.4.3 super ..................................................................................................................60

3.5 基本语句 ......................................................................................................................60

3.6 函数和结构 ..................................................................................................................63

3.6.1 函数 ...................................................................................................................63

·5·

3.6.2 结构 ...................................................................................................................65

3.7 系统对象 ......................................................................................................................66

3.7.1 Error ..................................................................................................................66

3.7.2 Message.............................................................................................................68

3.8 命名规则 ......................................................................................................................70

3.9 数据库支持 ..................................................................................................................71

3.9.1 PowerBuilder 中嵌入式 SQL 语句 ..................................................................71

3.9.2 指示器变量 .......................................................................................................74

3.9.3 SQL 语句的错误处理 ......................................................................................75

3.9.4 事务管理语句 ...................................................................................................75

3.9.5 利用存储过程操作数据 ...................................................................................77

3.9.6 利用游标操作数据 ...........................................................................................79

3.9.7 使用动态的 SQL ..............................................................................................82

3.9.8 大文本和大二进制数据的处理 .......................................................................89

3.10 PowerScript 编程环境 ...............................................................................................90

3.10.1 Script 子窗口 ..................................................................................................90

3.10.2 定制编程环境 .................................................................................................92

3.10.3 编程工具和编译 .............................................................................................95

3.10.4 对象浏览器 .....................................................................................................97

3.10.5 函数画板的使用 .............................................................................................97

3.10.6 结构画板的使用 .............................................................................................98

3.11 PowerScript 编程实例 ...............................................................................................98

3.12 练习题 ......................................................................................................................100

第 4 章 菜单的设计与使用 ........................................................................101

4.1 设计菜单 ....................................................................................................................101

4.1.1 菜单的基本术语 .............................................................................................101

4.1.2 菜单的类型 .....................................................................................................102

4.1.3 Menu Printer(菜单画板)工作区 .....................................................................102

4.1.4 创建菜单图 .....................................................................................................103

4.2 设计工具栏 ................................................................................................................105

4.2.1 为菜单添加工具栏 .........................................................................................105

4.2.2 在程序中管理工具栏 .....................................................................................106

4.3 菜单的使用与管理 ....................................................................................................109

4.3.1 把菜单挂到窗口上 .........................................................................................109

4.3.2 为菜单项添加脚本 .........................................................................................111

4.4 菜单设计实例 ............................................................................................................115

4.5 练习题 ........................................................................................................................116

·6·

第 5 章 数据窗口 ...................................................................................... 117

5.1 创建数据窗口对象 ....................................................................................................117

5.1.1 选择数据窗口对象的显示风格 .....................................................................117

5.1.2 选择数据源 .....................................................................................................125

5.1.3 定义检索参数 .................................................................................................127

5.2 设置数据窗口 ............................................................................................................130

5.2.1 设计窗口 .........................................................................................................131

5.2.2 属性窗口 .........................................................................................................131

5.3 数据的处理 ................................................................................................................139

5.3.1 数据检索 .........................................................................................................139

5.3.2 数据排序 .........................................................................................................139

5.3.3 数据过滤 .........................................................................................................140

5.3.4 数据分组 .........................................................................................................140

5.3.5 数据更新 .........................................................................................................142

5.3.6 数据的校验 .....................................................................................................144

5.3.7 在数据窗口对象中添加对象 .........................................................................148

5.4 数据存储 ....................................................................................................................153

5.4.1 数据存储简介 .................................................................................................153

5.4.2 使用数据存储 .................................................................................................154

5.5 数据窗口应用实例 ....................................................................................................155

5.6 练习题 ........................................................................................................................158

第 6 章 窗口与控件 .................................................................................. 159

6.1 窗口 ............................................................................................................................159

6.1.1 窗口的类型 .....................................................................................................159

6.1.2 创建窗口 .........................................................................................................159

6.1.3 窗口的属性设置 .............................................................................................160

6.1.4 窗口的事件 .....................................................................................................164

6.1.5 窗口的函数 .....................................................................................................166

6.1.6 在窗口之间传递参数 .....................................................................................167

6.2 控件 ............................................................................................................................168

6.2.1 概述 .................................................................................................................168

6.2.2 CommandButton(命令按钮)和 PictureButton(图像按钮) ............................171

6.2.3 RadioButton(单选按钮)和 CheckBox(复选框) ............................................172

6.2.4 Static Text(静态文本) .....................................................................................173

6.2.5 SingleLineEdit(单行编辑框) ..........................................................................173

6.2.6 EditMask(掩码编辑框)...................................................................................173

6.2.7 ListBox(列表框)和 PictureListBox(图像列表框)..........................................175

6.2.8 ListView(列表视图) .......................................................................................177

·7·

6.2.9 下拉列表框和图像下拉列表框 .....................................................................178

6.2.10 Tab(标签控件) ..............................................................................................179

6.2.11 统计图 ...........................................................................................................181

6.2.12 TreeView(树型视图控件) ............................................................................184

6.2.13 其他控件 .......................................................................................................189

6.3 数据窗口控件 ............................................................................................................189

6.3.1 概述 .................................................................................................................189

6.3.2 访问数据窗口的数据 .....................................................................................191

6.3.3 访问数据窗口的对象 .....................................................................................192

6.3.4 数据窗口控件的事件 .....................................................................................195

6.3.5 数据窗口控件的函数 .....................................................................................198

6.4 用户对象 ....................................................................................................................202

6.4.1 概述 .................................................................................................................202

6.4.2 创建用户对象 .................................................................................................203

6.4.3 使用用户对象 .................................................................................................206

6.5 窗口与控件编程实例 ................................................................................................208

6.6 练习题 ........................................................................................................................210

第 7 章 使用库管理项目 ...........................................................................211

7.1 库的概念 ....................................................................................................................211

7.1.1 基本概念 .........................................................................................................211

7.1.2 生成及命名 .....................................................................................................211

7.1.3 搜索路径 .........................................................................................................212

7.2 库的组织原则 ............................................................................................................213

7.2.1 库的分配 .........................................................................................................213

7.2.2 库的大小 .........................................................................................................213

7.2.3 库的组织原则 .................................................................................................213

7.2.4 库的优化 .........................................................................................................213

7.3 库画板 ........................................................................................................................214

7.3.1 使用库画板 .....................................................................................................214

7.3.2 搜索库及其中的对象 .....................................................................................219

7.3.3 重新生成库实体 .............................................................................................221

7.3.4 导出和导入实体 .............................................................................................223

7.3.5 使用源代码编辑器 .........................................................................................225

7.3.6 创建动态库 .....................................................................................................225

7.3.7 打印库内容 .....................................................................................................226

7.4 利用库进行多人开发 ................................................................................................227

7.4.1 PowerBuilder 自带的检入、检出工具..........................................................228

7.4.2 使用版本控制系统 .........................................................................................235

7.5 库管理的应用实例 ....................................................................................................236

·8·

7.6 练习题 ........................................................................................................................238

第 8 章 数据管道 ...................................................................................... 239

8.1 概述 ............................................................................................................................239

8.1.1 数据管道的功能 .............................................................................................239

8.1.2 数据管道的使用方法 .....................................................................................239

8.2 创建数据管道对象 ....................................................................................................240

8.2.1 建立数据管道的步骤 .....................................................................................240

8.2.2 修改数据管道 .................................................................................................241

8.2.3 执行数据管道 .................................................................................................243

8.3 在应用程序中使用数据管道 ....................................................................................244

8.3.1 数据管道用户对象 .........................................................................................244

8.3.2 在应用程序中创建管道对象 .........................................................................247

8.4 应用实例 ....................................................................................................................254

8.5 练习题 ........................................................................................................................256

第 9 章 在 PowerBuilder 9.0 中开发 JSP.................................................. 257

9.1 JSP 简介 .....................................................................................................................257

9.1.1 JSP 的工作方式 ..............................................................................................257

9.1.2 JSP 应用程序逻辑及其内容 ..........................................................................258

9.2 在 PowerBuilder 9.0 中使用 JSP 开发向导..............................................................258

9.2.1 服务器类型说明 .............................................................................................258

9.2.2 常用配置命令 .................................................................................................258

9.2.3 配置常用宏命令 .............................................................................................259

9.3 JSP 页面设计 .............................................................................................................259

9.3.1 JSP 页面设计元素 ..........................................................................................259

9.3.2 页面标识 .........................................................................................................259

9.3.3 JSP 指令 ..........................................................................................................260

9.3.4 在 JSP 页面中加入一条指令 .........................................................................261

9.3.5 添加 applets 和 JavaBeans..............................................................................262

9.3.6 声明 .................................................................................................................263

9.3.7 JSP 中的程序段 ..............................................................................................264

9.3.8 标签 .................................................................................................................267

9.3.9 错误控制 .........................................................................................................267

9.4 JSP 编程实例 .............................................................................................................268

9.5 练习题 ........................................................................................................................272

第 10 章 使用 Web 服务 ........................................................................... 273

10.1 Web 服务概述 .........................................................................................................273

10.1.1 Web 服务体系框架.......................................................................................273

·9·

10.1.2 Web 服务的特点 ..........................................................................................274

10.1.3 Web 服务核心技术简介 ..............................................................................274

10.2 Web Services 的创建和配置 ...................................................................................276

10.2.1 EAServer 及其 WST 简介............................................................................276

10.2.2 EAServer 安装与配置管理 ..........................................................................277

10.2.3 Web 服务创建实例 ......................................................................................281

10.3 Web Services 应用实例 ...........................................................................................290

10.3.1 安装 PowerBuilder 9.0.1 企业版升级包......................................................290

10.3.2 使用向导创建 Web 服务代理对象 .............................................................290

10.3.3 调用 Web 服务 .............................................................................................293

10.4 练习题 ......................................................................................................................296

第 11 章 PBNI 及第 3 方应用服务 ............................................................297

11.1 PBNI 及第 3 方应用简介 ........................................................................................297

11.1.1 关于 PBNI.....................................................................................................297

11.1.2 PBNI 的基本元素 .........................................................................................298

11.1.3 PBNI 的软件开发包(SDK) ..........................................................................307

11.1.4 比较 PBNI 和 JNI .........................................................................................307

11.2 PowerBuilder 与 C++的接口...................................................................................308

11.2.1 创建一个 PowerBuilder 扩展.......................................................................308

11.2.2 使用一个 PowerBuilder 扩展.......................................................................310

11.2.3 创建和使用可视化扩展 ...............................................................................311

11.2.4 在 C++应用程序中调用 PowerScript ..........................................................313

11.2.5 C++调用 PowerBuilder 对象........................................................................314

11.2.6 处理 PowerBuilder 消息...............................................................................318

11.3 引导扩展�PB 与 Java 及第 3 方服务的中介 .......................................................318 11.3.1 关于引导扩展 ...............................................................................................318 11.3.2 开发引导扩展 ...............................................................................................319 11.3.3 为 Java 类创建 PowerBuilder 代理..............................................................320 11.3.4 从 PowerBuilder 中调用 Java 类..................................................................320

11.4 PBNI 应用实例 ........................................................................................................321 11.4.1 非可视扩展实例——Ping............................................................................321 11.4.2 引导扩展实例——从 PowerBuilder 中调用 JAVA....................................323

11.5 练习题 ......................................................................................................................328

第 12 章 文档对象模型 .............................................................................329

12.1 PBDOM 对象...........................................................................................................329 12.2 PBDOM_Attribute 对象 ..........................................................................................330

12.2.1 定义 ...............................................................................................................330 12.2.2 方法 ...............................................................................................................331

·10·

12.3 PBDOM_Builder 对象 .............................................................................................332

12.3.1 PBDOM_Builder 对象定义 ..........................................................................332

12.3.2 PBDOM_Builder 对象方法 ..........................................................................333

12.4 PBDOM_CDATA 对象 ...........................................................................................333

12.4.1 PBDOM_CDATA 对象定义 ........................................................................333

12.4.2 PBDOM_CDATA 对象方法 ........................................................................334

12.5 PBDOM_CharacterData 对象..................................................................................335

12.5.1 PBDOM_CharacterData 对象定义...............................................................335

12.5.2 PBDOM_CharacterData 对象方法...............................................................336

12.6 PBDOM_COMMENT 类 ........................................................................................337

12.6.1 PBDOM_COMMENT 类定义 .....................................................................337

12.6.2 PBDOM_COMMENT 类方法 .....................................................................337

12.7 PBDOMDoctype 类 .................................................................................................338

12.7.1 PBDOMDoctype 类定义 ..............................................................................338

12.7.2 PBDOMDoctype 类方法 ..............................................................................338

12.8 PBDOMDocument 类 ..............................................................................................339

12.8.1 PBDOMDocument 类定义 ...........................................................................339

12.8.2 PBDOMDocument 类方法 ...........................................................................339

12.9 PBDOM_ELEMENT 类 ..........................................................................................341

12.9.1 PBDOM_ELEMENT 类定义 .......................................................................341

12.9.2 PBDOM_ELEMENT 类方法 .......................................................................341

12.10 PBDOM_ENTITYREFERENCE 对象..................................................................342

12.10.1 PBDOM_ENTITYREFERENCE 类定义...................................................342

12.10.2 PBDOM_ENTITYREFERENCE 类方法...................................................342

12.11 PBDOM_Exceptions 类 .........................................................................................343

12.12 PBDOM_Object 类 ................................................................................................343

12.12.1 PBDOM_Object 类定义 .............................................................................343

12.12.2 PBDOM_Object 类方法 .............................................................................343

12.13 PBDOM_ProcessingInstruction 类 ........................................................................344

12.13.1 PBDOM_ProcessingInstruction 类定义 .....................................................344

12.13.2 PBDOM_ProcessingInstruction 类方法 .....................................................345

12.14 PBDOM_Text 类....................................................................................................345

12.14.1 PBDOM_Text 类定义.................................................................................345

12.14.2 PBDOM_Text 类方法.................................................................................346

12.15 练习题 ....................................................................................................................346

第 13 章 人事管理系统 ............................................................................. 347

13.1 系统设计 ..................................................................................................................347

13.1.1 Target(目标)设计 ..........................................................................................347

13.1.2 开发设计理念 ...............................................................................................347

·11·

13.1.3 开发运行环境 ...............................................................................................347

13.1.4 功能分析与模块设计 ...................................................................................347

13.2 数据库设计 ..............................................................................................................347

13.2.1 概念设计 .......................................................................................................347

13.2.2 逻辑设计 .......................................................................................................349

13.2.3 物理设计 .......................................................................................................349

13.3 系统其他部分 ..........................................................................................................350

13.3.1 创建主菜单 ...................................................................................................350

13.3.2 创建 MDI 窗口 .............................................................................................351

13.3.3 创建父窗口 ...................................................................................................352

13.3.4 创建关于窗口 ...............................................................................................353

13.4 人事管理子系统 ......................................................................................................353

13.4.1 创建数据窗口对象 .......................................................................................353

13.4.2 创建窗体 .......................................................................................................355

13.4.3 与主菜单联系 ...............................................................................................358

13.5 工资管理子系统 ......................................................................................................358

13.5.1 创建数据窗口对象 .......................................................................................358

13.5.2 创建窗体 .......................................................................................................358

13.5.3 与主菜单联系 ...............................................................................................361

13.6 考勤管理子系统 ......................................................................................................361

13.6.1 创建数据窗口对象 .......................................................................................361

13.6.2 创建窗体 .......................................................................................................362

13.6.3 与主菜单联系 ...............................................................................................366

13.7 小结 ..........................................................................................................................366

第 14 章 学生成绩管理信息系统开发实例 ................................................367

14.1 系统设计 ..................................................................................................................367

14.1.1 Target(目标)设计 ..........................................................................................367

14.1.2 开发设计理念 ...............................................................................................367

14.1.3 开发运行环境 ...............................................................................................367

14.1.4 功能分析与模块设计 ...................................................................................367

14.2 数据库设计 ..............................................................................................................367

14.2.1 概念设计 .......................................................................................................367

14.2.2 逻辑设计 .......................................................................................................368

14.2.3 物理设计 .......................................................................................................369

14.3 信息修改子系统 ......................................................................................................376

14.3.1 创建数据窗口对象 .......................................................................................376

14.3.2 创建信息修改子窗口 ...................................................................................376

14.3.3 与主菜单联系 ...............................................................................................377

14.4 成绩录入子系统 ......................................................................................................378

·12·

14.5 成绩查询子系统 ......................................................................................................379

14.5.1 创建数据窗口对象 .......................................................................................379

14.5.2 创建数据窗口 ...............................................................................................379

14.5.3 与主菜单联系 ...............................................................................................380

14.6 成绩统计及打印子系统 ..........................................................................................380

14.6.1 创建数据窗口对象 .......................................................................................380

14.6.2 创建窗体 .......................................................................................................381

14.6.3 添加代码 .......................................................................................................382

14.6.4 与主菜单联系 ...............................................................................................383

14.6.5 统计某班的成绩模块 ...................................................................................383

14.7 小结 ..........................................................................................................................384

第 15 章 网上采购管理信息系统开发实例 ................................................ 385

15.1 系统设计 ..................................................................................................................385

15.1.1 目标设计 .......................................................................................................385

15.1.2 开发设计理念 ...............................................................................................385

15.1.3 开发运行环境 ...............................................................................................385

15.1.4 功能分析与模块设计 ...................................................................................386

15.2 数据库设计 ..............................................................................................................386

15.2.1 需求分析 .......................................................................................................386

15.2.2 概念设计 .......................................................................................................387

15.2.3 逻辑与物理设计 ...........................................................................................390

15.3 系统主窗口 ..............................................................................................................392

15.3.1 C/S 下主窗口 ................................................................................................392

15.3.2 B/S 下主窗口 ................................................................................................395

15.4 订单管理子系统 ......................................................................................................399

15.4.1 采购计划申报 ...............................................................................................399

15.4.2 计划平衡 .......................................................................................................405

15.5 报价比价子系统 ......................................................................................................409

15.5.1 网上报价 .......................................................................................................409

15.5.2 比价与草签合同 ...........................................................................................414

15.6 合同审核子系统 ......................................................................................................417

15.6.1 主窗口 ...........................................................................................................417

15.6.2 合同审批业务实现 .......................................................................................419

15.7 付款子系统 ..............................................................................................................420

15.7.1 付款通知单生成 ...........................................................................................420

15.7.2 付款通知单查询 ...........................................................................................422

15.8 小结 ..........................................................................................................................424

·13·

第 16 章 仓储管理信息系统开发实例........................................................425

16.1 系统设计 ..................................................................................................................425

16.1.1 Target(目标)设计 ..........................................................................................425

16.1.2 开发设计理念 ...............................................................................................425

16.1.3 开发运行环境 ...............................................................................................425

16.1.4 功能分析与模块设计 ...................................................................................425

16.2 数据库设计 ..............................................................................................................426

16.2.1 概念设计 .......................................................................................................426

16.2.2 逻辑设计 .......................................................................................................429

16.2.3 物理设计 .......................................................................................................432

16.3 建立应用程序 ..........................................................................................................435

16.3.1 创建应用程序对象 .......................................................................................435

16.3.2 创建主菜单 ...................................................................................................435

16.3.3 主窗口 ...........................................................................................................435

16.3.4 需要创建的对象 ...........................................................................................437

16.4 系统设置模块设计 ..................................................................................................439

16.4.1 创建父窗口对象 ...........................................................................................439

16.4.2 为 w_modfather 父窗口添加用户自定义事件及其脚本............................440

16.4.3 创建子窗口对象 ...........................................................................................440

16.4.4 创建数据窗口对象 .......................................................................................441

16.4.5 为系统主菜单 m_main 添加脚本 ................................................................442

16.5 入库管理 ..................................................................................................................443

16.5.1 入库管理模块设计 .......................................................................................443

16.5.2 新到料日志 ...................................................................................................444

16.5.3 质检签发 .......................................................................................................445

16.5.4 入库单 ...........................................................................................................446

16.6 出库管理 ..................................................................................................................448

16.6.1 创建窗口对象和数据窗口 ...........................................................................448

16.6.2 出库单填写 ...................................................................................................449

16.6.3 出库单审核 ...................................................................................................449

16.7 库存管理 ..................................................................................................................451

16.7.1 库存查询 .......................................................................................................451

16.7.2 统计报表 .......................................................................................................452

16.8 小结 ..........................................................................................................................453

参考文献.....................................................................................................454

第 1 章 Pow erB uilder 9.0 基础和集成环境

1.1 PowerBuilder 9.0 的基本概念

从总体上讲,PowerBuilder 是一个图形界面的应用程序开发环境,之所以说它是一个

开发环境,而不仅是开发工具,是因为 PowerBuilder 的功能比一般开发工具要强大得

多。开发人员不仅能用它来建立各种方便、易用的应用程序,还可以通过它修改后台的数

据库,以及利用内部函数建立能和其他应用程序通信的各种应用程序。由于使用

PowerBuilder 开发的应用程序充分利用了图形用户接口(GUI)的优点,所以它也被认为是

一个图形工具。

1.1.1 基本概念

针对用户的不同需要,PowerBuilder 9.0 有 3 个不同的版本。

(1) PowerBuilder 9.0 桌面版。

(2) PowerBuilder 9.0 专业版。

(3) PowerBuilder 9.0 企业版。

PowerBuilder 9.0 桌面版是为单个开发人员设计的可在独立开发环境中使用的强大工

具。它可以使用其内置的 32 位关系型数据库 Sybase SQL Anywhere 或其他的桌面数据

库,创建单用户应用,能大大提高用户的效率。

PowerBuilder 9.0 专业版可以为建立部门级应用的小组提供完整的开发环境,并可通

过优化的 ODBC 接口与多种桌面系统和服务器数据库连接。

PowerBuilder 9.0 企业版除了具有桌面和专业版本的全部功能以外,还提供了可提高

开发大型项目效率的许多功能。

这 3 个不同版本的主要差异是:在提供的对大型数据库的专用接口上,企业版最全,

专业版次之,而桌面版几乎没有提供。

1.1.2 PowerBuilder 9.0 的安装环境

由于 PowerBuilder 支持在多个平台上的开发和使用,所以在安装前,除了要有

PowerBuilder 9.0 光盘外,还应检查所使用的平台。这里简单介绍一下基于 Windows 平台

的计算机软硬件典型配置。

(1) 486 以上的 CPU。

(2) 16 MB 以上的内存。

(3) CD-ROM 驱动器。

(4) VGA 显示器。

(5) 至少 45 MB 以上的硬盘空间,具体大小依赖于所选部件。

Pow erBuilder9.0 基础应用与系统开发

— 2 —

(6) Windows 95/98,或 Windows NT4.0/5.0 操作系统。

1.1.3 PowerBuilder 9.0 的新特性

1. 支持快速应用开发的 JSP 编辑器

低版本 PowerBuilder 只能开发 PowerBuilder 的应用,而 PowerBuilder 9.0 则可以生成

JSP 的应用,从而可以进行基于 Internet 的开发。

2. 支持 XML

对 于 XML 的 支 持 具 体 表 现 在 两 方 面 : 一 是 支 持 DataWindow ; 二 是 支 持

DOM(Document Object Model)。

(1) 支持 DataWindow:PowerBuilder 就是通过 DataWindow 对 XML 进行支持的。

DataWindow 可以同时导入多个 XML 模版,从多个方面来展示 DataWindow 的数据模

式。同时,也可以通过将 DataWindow 文件存为 XML 文件导出,从而使数据真正成为

Web Service 的资源。众所周知,DataWindow 是 Sybase 的专利技术,原来的产品中,

DataWindow 只能通过 PowerBuilder 使用,而 PowerBuilder 9.0 通过将 DataWindow 转化为

XML 文件,使之成为 Internet 环境下的供大家分享的资源,同时,还可以将外部的 XML

文件加载进来,转化为 DataWindow 文件,不能不说这是一个突破性的进步。

(2) 支持 DOM(Document Object Model):低版本 PowerBuilder 支持 XML 时要通过

MSXML。而 MSXML 需要调用底层数据,这过于复杂,效率太低。PowerBuilder DOM

是 PowerBuilder 9.0 的一个功能模块,可以跨越 Wintel 和 Unix 平台,它完全可以取代

MSXML,并且功能更先进,更易用,是更为简单的调用数据接口。

3. PBNI(PowerBuilder Native Interface)

PBNI 提供一个 Native Interface,使 PowerBuilder 提高对外扩展能力,能够访问任何

类型的外部应用,也允许外界访问 PowerBuilder,从而达到方便调用任何外部应用资源的

效果。相比之下,传统的 PowerBuilder 产品是相对封闭的开发环境。例如在做底层数据

通信时,受到 DLL 调用模式的限制,接口不是很透明,调试和使用都不是很方便。PBNI

使 PowerBuilder 能够灵活调用外界任何动态链接库或底层函数等,同时外部应用可以反

调 PowerBuilder 9.0 中的对象,使 IDE(集成开发环境)可以更加有机地结合外部环境。

4. EJB Client

PowerBuilder 9.0 可以通过 EJB Client 来调用任何一个 EJB 组件,具体是通过代理

(PROXY)的方式来调用。EJB Client 成为 PowerBuilder 应用端与 Java 虚拟机之间的有效桥

梁。

5. PowerBuilder 9.0 与 Web Service 的关系

Web Service 作为发展中的概念,PowerBuilder 9.0 支持 Java 与.NET 两大阵营。

6. PowerBuilder 9.0 与.NET

未来 PowerBuilder 和.NET 的支持将分为以下几个阶段。

(1) .NET 是一个集成的概念,PowerBuilder 9.0 以 Web Service 的方式支持.NET。

第 1章 Pow erBuilder9.0 基础和集成环境

— 3 —

(2) 这个阶段将引入 DataStore.NET 和 DataWindow.NET。

(3) 具备访问整个.NET 平台的能力。

(4) PowerBuilder 开发人员将能使用建立在.NET 架构中所附加的全部功能。

7. 其他增强功能

(1) 可存为 PDF 文件。

(2) 增强 Debugger。

(3) 增强版本控制。

1.1.4 术语

当开始学习一种开发工具或掌握一门语言时,都应该先熟悉其特有的术语,这是一个

重要的前提。下面简单介绍一下 PowerBuilder 9.0 中的术语。

1. Workspace(工作区)

Workspace(工作区)是 PowerBuilder 引入的新概念。在 PowerBuilder 以前的版本中,

只能在一个应用中处理对象,而引入工作区后,可以在一个工作区中包含若干个目标,每

个目标中又包含一个或多个应用,这样,当基于一个工作区工作时,就可以同时在多个应

用中操纵对象。

2. Target(目标)

Target(目标)也是 PowerBuilder 引入的新概念。在一个工作区中,可以加入任意多个

目标。PowerBuilder 中的目标分为两类:PowerScript Target 和 Web Target。前者可以是任

一种常使用的应用类型,如可执行应用或 EAServer 组件。后者则是一个 Web 应用,它包

含了建立一个 Web 站点的所有元素,如 HTML 文件、脚本、图像等。

3. System Tree(系统树)

System Tree(系统树)同样是 PowerBuilder 引入的新概念。它可以充当整个应用程序的

总控中心,显示系统树的窗口称之为系统树窗口。在系统树窗口中,可以获取开发过程中

的信息(如新增的数据窗口、窗口中新加入的函数),都可以在系统树中体现出来。除此之

外,还可以方便地拖动对象,将其放入画板视图(如脚本视图、LayOut 视图、HTML 编辑

器等),立即使用。

4. Application Object(应用对象)

Application Object(应用对象 )是一个应用程序的 Entry Point(入口点 )。任何一个

PowerBuilder 程序都是从应用对象开始执行的。在应用对象中,通常定义的是应用级的行

为,如整个应用程序的默认字体、背景颜色、处理系统错误的事件等。

5. Painter(画板)

在 PowerBuilder 中,Painter(画板)是用来编辑相应对象的编辑器。例如,在 Window

Printer(窗口画板)中设计修改窗口,在 Menu Painter(菜单画板)中定制菜单等。总之,每一

种对象都有专门用于创建并修改它的地方。由于画板的存在,使功能划分更加明确,处理

对象更加方便。

Pow erBuilder9.0 基础应用与系统开发

— 4 —

6. Window(窗口)

Window(窗口)是用户和应用程序交互的界面环境。开发人员设计的程序,在最终用

户面前体现的是一个个方便使用、功能强大、美观友好的窗口组合。

7. Control(控件)

Control(控件)在各种可视化开发工具中都是十分重要的。作为应用程序的组成部分,

每个控件都具有自己特殊的功能和性质,它和窗口一起构成应用程序界面,并可完成多种

操作。用户可以在控件的属性窗口或在程序中灵活控制和使用控件的属性和方法,以使其

满足特定要求。

8. DB Profile(数据库描述文件)

DB Profile(数据库描述文件)在编写应用程序之前,需要和数据库进行连接。使用控

制面板定义好数据源之后,要在 PowerBuilder 中使用这些数据源,就要配置数据库描述

文件。该文件的主要作用是用来设置具体的数据库连接参数,包括数据源名、用户 ID、

密码及连接类型等。

9. DataWindow(数据窗口)

DataWindow(数据窗口)是 PowerBuilder 的专利技术,它的数据处理功能十分强大。

在 PowerBuilder 中,对数据的处理工作基本上是通过数据窗口来实现的。数据窗口包括

数据窗口对象和数据窗口控件两部分。数据窗口对象可以以图形化的方式对数据库中的数

据进行检索、修改,而不必使用 SQL 语句。数据窗口控件是装载数据窗口对象的容器,

只有使用该控件,设计人员才可以直观地看到所定义的数据窗口对象。

10. Library(库)

Library(库)是 PowerBuilder 中用来存放对象的地方。使用库可以避免管理众多复杂与混乱

的对象,使对象存放井然有序,还可以方便地对对象进行诸如版本控制、导入导出等操作。

1.2 PowerBuilder 的环境介绍

1.2.1 整体界面

进入 PowerBuilder 9.0 的开发环境后,会看到如图 1-1 所示的窗口,其中包括 5 个部

分:标题栏、菜单栏、工具栏、客户区、状态栏。在该窗口中,可以新建、继承或是打开

任意一个 Painter(画板)来创建、编辑对象。

1.2.2 菜单栏

PowerBuilder 9.0 的菜单栏包括 File、Run、Tools、Window 和 Help 等 5 个主菜单

项。需要注意的是,这 5 个菜单项是在进入 PowerBuilder 之后且没有启动任何功能之前

所显示的菜单,当打开某个具体的画板后,菜单会进行相应的变化。表 1-1 给出这些菜单

项功能的简要介绍。

第 1章 Pow erBuilder9.0 基础和集成环境

— 5 —

菜单条

客户区

工具条

状态栏

标题栏

图 1-1 PowerBuilder 9.0 整体界面

表 1-1 开发环境的菜单说明

主菜单项 子菜单项 子菜单项功能的说明 New 创建新的 PowerBuilder 对象Inherit 使用继承创建新的对象

Open 打开一个 PowerBuilder 对象Run/Preview 运行/预览当前对象

Open Workspace 打开一个工作区Printer Setup 进行打印机的设置Recent Objects 显示最近使用过的对象

Recent Workspaces 显示最近使用过的工作区Recent Connections 显示最近使用过的连接

File

Exit 退出 PowerBuilder 开发环境Incremental Build Workspace 增量重建工作区Full Build Workspace 全部重建工作区

Deploy Workspace 配置当前工作区Debug 调试最近的目标

Select and Debug 选择目标并进行调试Run 运行最近的目标

Select and Run 选择目标并运行Skip Operation 跳过(建立/配置/搜索)操作Stop Operation 停止(建立/配置/搜索)操作

Next Error/Message 在输出窗口中跳到下一个错误/消息

Run

Previous Error/Message 在输出窗口中跳到前一个错误/消息

Toolbars 显示设置工具栏的对话框 Keyboard Shortcuts 显示系统预设的功能快捷键 System Options 显示系统选项 To Do List 显示 To Do List 窗口 Browser 浏览当前目标的所有对象 Library Painter 打开库画板 Database Profile 对数据库描述文件进行设置 EAServer Profile 对 EAServer 描述文件进行设置 Database Painter 打开数据库画板

Tools

File Editor 打开文件编辑器 Tile Vertical 竖向排列所有打开的画板 Tile Horizontal 横向排列所有打开的画板 Layer 以层的方式排列所有打开的画板 Cascade 层叠排列所有打开的画板 Arrange Icons 重排图标 Close All 关闭所有打开的画板 System Tree 显示或隐藏系统树窗口 Output 显示或隐藏输出窗口

Window

Clip 显示或隐藏剪贴板窗口

Pow erBuilder9.0 基础应用与系统开发

— 6 —

(续表)

主菜单项 子菜单项 子菜单项功能的说明 Contents 显示 PowerBuilder 的帮助内容 Welcome to PowerBuilder 8.0 显示 PowerBuilder 8.0 新功能帮助 Sybase Web Site 连接 Sybase 公司站点 Electronic Case Managements 显示 PowerBuilder 的电子帮助信息 Sybase Online Books Site 显示在线帮助

Help

About PowerBuilder 显示 PowerBuilder 版权信息

1.2.3 工具栏

工具栏将常用的菜单选项用图标的形式列出,每一个图标对应一个功能。通常,工具

栏位于菜单下面,单独排成一行,它形象直观而且便于使用。表 1-2 是对工具栏中各个图

标的说明。

表 1-2 工具栏中各个图标的说明

图 标 名 称 说 明

New 创建新的 PowerBuilder 对象

Inherit 使用继承创建新的对象

Open 打开一个 PowerBuilder 对象

Run/Preview 运行/预览当前对象

System Tree 显示或隐藏系统树窗口

Output 显示或隐藏输出窗口

Next Error/Message 在输出窗口中跳到下一个错误/消息

Previous Error/Message 在输出窗口中跳到前一个错误/消息

To Do List 显示 To Do List 窗口

Browser 浏览当前目标的所有对象

Clip 显示或隐藏剪贴板窗口

Library 打开库画板

DB Profile 指明数据库描述文件

EAServer 指明 EAServer 描述文件

Datebase 运行数据库画板

Edit 编辑文件

Incremental Build Workspace 增量重建工作区

Full Build Workspace 全部重建工作区

Deploy Workspace 配置当前工作区

Skip Operation 跳过(建立/配置/搜索)操作

Stop Operation 停止(建立/配置/搜索)操作

Debug 调试最近的目标

Select and Debug 选择目标并进行调试

Run 运行最近的目标

Select and Run 选择目标并运行

Exit 退出 PowerBuilder 开发环境

第 1章 Pow erBuilder9.0 基础和集成环境

— 7 —

需要说明的是,PowerBuilder 工具栏中的工具是可以选择的,也就是说,上述这些工

具并不是固定不变的。但是由于这些工具比较常用,因此默认时系统会列出它们。要选择

工具,可以使用 Tools|Toolbars 命令,系统将弹出如图 1-2 所示的对话框。

图 1-2 Toolbars 对话框

该对话框的用途是配置工具栏。其中,Move 组合框用来设定工具栏在工作区中的位

置,选中 Show Text 时在工具栏下显示文字,选中 Show PowerTips 将以弹出方式显示各

个工具的提示信息。若在工具栏下显示文字,Font Name 用来选择字体,Font Size 用来选

择字体大小。

要进行工具栏中工具的选择,单击该对话框中的 Customize 按钮,出现 Customize 对

话框,如图 1-3 所示。在 Select Palette 中列出了系统提供的所有工具,而 Current toolbar

中则列出了当前的工具。可以将一个图标从 Selected Palette 拖动到 Current toolbar 中,以

增加当前使用的工具图标组,当不需要时,可将某些工具从 Current toolbar 窗口拖回到

Select Palette 中。

图 1-3 Customize 对话框

Pow erBuilder9.0 基础应用与系统开发

— 8 —

1.2.4 Painter(画板)简介

Painter(画板)是用来编辑相应对象的编辑器。在此将对 PowerBuilder 9.0 的一些常用

画板做一简要介绍。

1. Application Painter(应用画板)

Application Painter(应用画板)用来详细描述有关应用的信息。在应用画板中,可以为

应用级的事件编写脚本或修改应用的属性(例如字体、图标等)。应用画板的示例如图 1-4

所示。

图 1-4 Application Painter(应用画板)

2. Window Painter(窗口画板)

应用程序中的窗口设计都是在 Window Painter(窗口画板)中进行的。在窗口画板中,

不仅可以设计窗口,还可以指定与该窗口相连的菜单,在窗口中加入控件,以及修改窗口

和控件的属性(例如窗口的类型、大小及定位)等。窗口画板的示例如图 1-5 所示。

图 1-5 Window Painter(窗口画板)

3. Menu Painter(菜单画板)

Menu Painter(菜单画板)的作用是创建应用程序中使用到的菜单。PowerBulder 中可以

定义多种菜单,包括弹出式菜单、级联菜单、下拉菜单等。使用菜单来打开相应窗口的脚

本也是在菜单画板中进行的,菜单画板如图 1-6 所示。

第 1章 Pow erBuilder9.0 基础和集成环境

— 9 —

图 1-6 Menu Painter(菜单画板)

4. DataWindow Painter(数据窗口画板)

DataWindow Painter(数据窗口画板)是 PowerBuilder 中的精华,可以利用它从数据库

或其他数据源中检索、处理数据。在数据窗口画板中还可以定义有效性规则、排序和过滤

数据、设置数据窗口的属性(包括默认颜色、打印说明等)。数据窗口画板如图 1-7 所示。

图 1-7 DataWindow 画板

5. Database Painter(数据库画板)

Database Painter(数据库画板)是专门用来操纵数据库的。通过它可以和数据库连接,

完成创建表、创建视图、操作数据、执行 SQL 语句进行查询等工作。图 1-8 给出数据库

画板的示意图。

图 1-8 Database Painter(数据库画板)

Pow erBuilder9.0 基础应用与系统开发

— 10 —

6. Structure Painter(结构画板)

当程序中需要使用全局结构时,可以在 Structure Painter(结构画板)中予以定义,也就

是说,可以在结构画板里定义结构体中元素的类型和变量名。但要注意:local(局部)结构

不使用结构画板来定义。图 1-9 是结构画板的示意图。

图 1-9 Structure Painter(结构画板)

7. Function Painter(函数画板)

应用程序中使用的全局函数是在 Function Painter(函数画板)中定义的,在函数画板

中,可以指定函数的名称、类型、返回值,并编写函数代码等。图 1-10 是函数画板的示

意图。

图 1-10 Function Painter(函数画板)

8. User Object Painter(用户对象画板)

User Object(用户对象)分为可视和不可视两种。可视用户对象就是可重复使用的、包

含一些动作的控件或控件的集合;不可视用户对象则是可重复使用的、不包含可视组件的

一组处理规则,如类用户对象。

9. Library Painter(库画板)

Library Painter(库画板)是用来管理 PowerBuilder 库及库中对象的,它像是一个控制部

门,在库画板中可以将各种对象在相应的画板中打开。图 1-11 是库画板的示意图,其中

包含一个 Tree 视图和一个 List 视图,用来显示工作区、目标和库。

第 1章 Pow erBuilder9.0 基础和集成环境

— 11 —

图 1-11 Library 画板

10. Project Painter(工程画板)

Project Painter(工程画板)用来创建并维护 PowerBuilder 工程。在工程画板中,可以指

定可执行文件名、资源文件名、版本信息等。图 1-12 是工程画板的一个示例。

图 1-12 Project 画板

11. SQL Select Painter(或 Select Painter)

当为建立一个数据窗口或数据管道而需要选择数据源或编辑这个数据源时,通常会使

用到 SQL Select Painter。借助于 SQL Select Painter 中的表视图,可以方便地选择表中的

列。图 1-13 是 SQL Select Painter 的示意图。

图 1-13 SQL Select Painter

Pow erBuilder9.0 基础应用与系统开发

— 12 —

12. Query Painter(查询画板)

使用 Query Painter(查询画板)可以以图形的方式来定义和保存 SQL 语句。还可以在查

询画板中定义检索参数、排序、选择等。查询画板的示意图如图 1-14 所示。

图 1-14 Query Painter(查询画板)

13. Data Pipeline Painter(数据管道画板)

Data Pipeline Painter(数据管道画板)的用途是在不同的数据库之间或是在同一个数据

库的不同表之间转移数据。图 1-15 是数据管道画板的一个示例。

图 1-15 Data Pipeline Painter(数据管道画板)

1.3 练习题

(1) PowerBuilder 9.0 中有哪些新特性?

(2) 用 PowerBuilder 建立数据库连接应该使用哪个画板?

(3) 管理 PowerBuilder 库中对象的是哪个画板?

(4) 数据管道画板的主要用途是什么?

(5) 请简要介绍数据窗口的概念。

第 2 章 使用 Pow erB uilder 操纵数据库

方便有效地操作数据库是 PowerBuilder 的重要特色之一。在 PowerBuilder 中,应用

程序既可以处理本地数据库 Adaptive Server Anywhere,也可以访问后台数据库,例如

Microsoft SQL Server、Oracle 等。PowerBuilder 提供了多种方式的数据库处理功能,例如

创建与修改表、增加表的属性、主键及外键的使用、视图的创建等。而存储过程和触发器

的使用更是极大地提高了数据库操作的效率。

本章将详细介绍数据库的创建、连接,表的操作及存储过程和触发器的使用等内容。

熟练掌握好这部分内容,对提高应用程序的开发效率及整体把握数据库有很大帮助。

2.1 使用数据库画板建立数据库

在用 PowerBuilder 访问数据库之前,应先使用相应的数据库管理系统(比如 Oracle,

Sybase 等)创建自己的数据库(通常需要数据库管理员在服务器上完成创建工作),然后通

过 ODBC 接口或专用接口把应用程序连接到数据库上。为此,PowerBuilder 提供了数据

库画板让用户做所有必需的后台工作。利用这个画板,用户能够创建和删除本地的

Sybase Server Anywhere 数据库。

2.1.1 Database(数据库画板)

单击 PowerBuilder 工具栏的 Database(数据库画板)图标 ,打开如图 2-1 所示的数据

库画板。

对象子窗口 列子窗口 布局子窗口 对象属性子窗口

图 2-1 Database Painter(数据库画板)

整个数据库画板分为四部分,即 Objects(对象子窗口)、Object Layout(布局子窗口)、

Object Detail(对象属性子窗口)和 Columns(列子窗口)。Objects 中显示当前系统安装的各种

Pow erBuilder9.0 基础应用与系统开发

— 14 —

数据库管理系统的接口及用户建立的数据库描述文件。布局子窗口主要显示表的整体信

息,在布局子窗口中还有另外一个标签页,即 Extended Attributes 标签页,它显示的是当

前系统定义的各种扩展属性。对象属性子窗口显示的是所选对象的属性描述。列子窗口显

示的是各个表的列。

打开数据库画板后,可以看到在工具栏上多了一排数据库专用工具栏,对其中工具图

标的说明见表 2-1。

表 2-1 数据库专用工具栏说明

图 标 名 称 说 明

Connect 连接数据库

Disconnect 断开与数据库的连接

Save 保存设置

Create Table 创建表

Drop Object 删除当前所选对象

Print 打印

Properties 显示所选对象的属性

Data Manipulation(Grid) 以 Grid(表格)格式显示当前表的数据

Data Manipulation(Tabular) 以 Tabular(列表)格式显示当前表的数据

Data Manipulation(Freeform) 以 Freeform(自由)格式显示当前表的数据

Pipeline 打开数据管道画板

Close 退出数据库画板

2.1.2 创建 Sybase Server Anywhere 数据库

使用数据库画板创建本地 Sybase Server Anywhere 数据库有以下几个步骤。

(1) 在图 2-1 中的 Objects 窗口中的 ODB ODBC 节点下,双击 Utilities 节点下的

Create ASA Database 选项,弹出如图 2-2 所示的 Create Adaptive Server Anywhere Database

对话框。

(2) 在 User ID 中输入用户名称,默认为 DBA。在 Password 中,输入用户口令,默

认为 sql。在 Database Name 中输入要创建的数据库名称。注意,在输入数据库名称时一

定要指明路径,否则将出现系统不能创建的情况。当然,也可以通过单击数据库名称编辑

框右侧按钮确定路径。如果选中 Prompt For Password During Connect 复选框,那么在连接

该数据库时,系统会要求输入用户名和密码,否则不要求。在组合框 Database Option

中,设置数据库的其他选项。其中,如果选中 Case Sensitive Values 复选框,则数据库中

的表名、列名等名称区分大小写,否则不区分;Use transaction log 复选框用于指示是否生

成日志文件,以登记用户对数据库的所有操作。日志文件主要用于数据库备份与恢复。

(3) 设置好上述选项后,单击 OK 按钮,便建立了一个数据库。

第 2 章 使用 Pow erBuilder操纵数据库

— 15 —

图 2-2 Create Adaptive Server Anywhere Database 对话框

2.1.3 删除 Sybase Server Anywhere 数据库

删除 Sybase Server Anywhere 数据库的步骤如下。

(1) 在数据库画板的对象子窗口中的 ODB ODBC 节点下双击 Utilities 节点下的 Delete

ASA Database 选项,弹出 Delete Local Database 对话框。

(2) 选择要删除的数据库,然后单击“打开”按钮。

(3) 系统询问是否真要删除,单击“是”按钮后删除指定的数据库。

注意

不能删除当前正在连接的数据库。如想删除当前连接的数据库,则先要断开连接,之

后再删除原来的数据库。

2.2 PowerBuilder 与数据库的连接

PowerBuilder 是专为各种数据库设计的客户端开发工具。它可以和其他数据库服务器

相连,从而构成客户/服务器体系结构。同时 PowerBuilder 也可以和 dBase、Access 等文

件类型的数据库相连,对这些数据文件进行操作。对于不同类型的数据源,PowerBuilder

提供了不同的接口软件进行连接。

PowerBuilder 可以通过两种数据库接口与数据库连接:PowerBuilder 的 ODBC 接口和

PowerBuilder 自带的数据库专用接口。

2.2.1 ODBC 接口

ODBC(Open Database Connectivity)是由微软提出的开放式数据库互连接口标准。

Pow erBuilder9.0 基础应用与系统开发

— 16 —

ODBC 接口以 SQL 作为标准的查询语言来存取所连接的数据源。它允许单个应用同时访

问多个不同的数据库管理系统(DBMS)。这使应用用户可以开发、编辑和发行应用软件而

不必考虑它所操作的数据库管理系统的类型究竟是什么。用户可以通过加载连接到不同数

据库的驱动程序来建立与各种数据库的连接。

目前,几乎所有的数据库管理系统都提供标准化的 ODBC 环境。开发人员通过采用

ODBC 应用程序编程方法,不再需要分别学习各种数据库系统的专门接口技术即可进行

程序的开发。这样就极大地减少了数据库系统的升级及移植的开发工作量。ODBC 接口

工作原理如图 2-3 所示。

PowerBuilder 开发环境

ODBC 接口(PBODBNO.DLL)

ODBC 驱动程序管理层(ODEC32.DLL)

Oracle 驱动 MSS 驱动 ASA 驱动

数据库 数据库 数据库

应用层

图 2-3 ODBC 接口工作原理图

从图 2-3 可以看出,一个 ODBC 连接由以下 4 层构成。

(1) 应用层:该层由 Sybase 提供,用于调用 ODBC 函数提交的 SQL 语句,并从数据

源中取回执行结果。PowerBuilder 使用统一的 ODBC 接口访问所有的 ODBC 数据源。

(2) ODBC 管理层:此层由微软提供,用来为应用程序安装、加载和卸载驱动程序。

(3) ODBC 驱动程序:此层由驱动程序厂商提供,用于处理 ODBC 函数调用,向指定

的数据源提交 SQL 请求,以及向应用程序返回结果。

(4) 数据源:此层由数据库厂商提供,用于存储和管理数据,主要由数据库中的数据

和数据库管理系统(DBMS)构成。

当 PowerBuilder 应用程序需要访问一个 ODBC 数据源时,它首先通过 PowerBuilder

的 ODBC 接口程序 PBODBn0.DLL 向 ODBC 管理程序 ODBC32.DLL 发出请求,管理程

序根据请求加载相应的 ODBC 驱动程序,然后 ODBC 驱动程序调用相关软件把 SQL 请求

提交到后台的 DBMS 中,最后 DBMS 把执行结果沿着相反的方向返回给最初发出请求的

应用程序。

在 PowerBuilder 开发环境中,通过 ODBC 接口访问数据库需要做以下几步工作。

(1) 安装 ODBC 驱动程序。

(2) 准备 ODBC 数据源。

(3) 定义数据库描述文件(DB Profile)。

2.2.2 专用接口

为了能快速地访问数据库中的数据, PowerBuilder 为一些大型关系数据库提供了专

用的数据库接口。这些专用接口针对具体的数据库管理系统(主要包括:Sybase、Oracle、

第 2 章 使用 Pow erBuilder操纵数据库

— 17 —

Informix、DB2 等)而设计。在 Windows 平台下,专用接口就是一个 DLL(动态链接库)。

它直接调用特定数据库的 API 访问数据库,而不需要经过其他中间层(如 ODBC),因此存

取速度非常快。通过 ODBC 连接到 Sybase 数据库和通过专用接口连接到 Sybase 数据库

上,其查询速度会相差十几倍。由于专用接口在设计上针对相应数据库的特点,使其能够

充分发挥特定数据库的优势,因此在使用大型数据库时,用户应该尽可能使用专用数据库

接口而不是 ODBC 接口。不同的数据库或同一数据库的不同版本,所使用的专用接口也

不同。PowerBuilder 专用数据库接口的工作原理如图 2-4 所示。

PowerBuilder 开发环境

专用数据库接口 DLL

数据库客户端软件

网络协议 由网络供应商或数据库

厂商提供

数据库

由数据库厂商提供

由 Sybase 提供

图 2-4 专用数据库接口工作原理图

从图 2-4 可以看出,通过 PowerBuilder 专用接口访问大型数据库需要做以下几步工

作。

(1) 安装并调通网络。PowerBuilder 专用接口都是用于网络上的大型数据库的。因

此,调通网络、建立网络环境是利用专用接口将 PowerBuilder 连接到大型数据库的第 1

步。另外,还必须选择数据库支持的网络软件、网络协议以及协议版本。

(2) 安装数据库,并且在网络环境中使数据库厂商提供的客户软件能够正常运行。例

如,对 Oracle 来说,客户端的 SQL*Plus 应该能正常地访问 Oracle。

(3) 安装指定数据库的专用接口。

(4) 定义数据库描述文件(Database Profile)。

2.2.3 数据库描述文件

数据库描述文件(Database Profile)是在 Database Profile 画板中定义的连接参数集合,

PowerBuilder 用它来调用特定的 ODBC 数据源或专用数据库接口。

2.2.3.1 创建数据库描述文件

在使用数据库之前,必须先创建数据库描述文件。

单击工具栏的数据库描述文件图标 ,弹出如图 2-5 所示的 Database Profiles 画板。

PowerBuilder 使用如图 2-5 所示的树状图样式窗口列出当前安装的所有的数据库接口

及用户建立的数据库描述文件。在这个窗口可以新建、删除、编辑数据库描述文件,连接

数据库,还可以导入、导出数据库描述文件。在图 2-5 的窗口中单击 New 按钮,弹出如

图 2-6 所示的数据库描述文件配置(使用 ODBC 数据源)窗口。

Pow erBuilder9.0 基础应用与系统开发

— 18 —

图 2-5 Database Profiles 画板

图 2-6 配置数据库描述文件

这个窗口带有 8 个标签页,这些标签页的作用主要是用来设置具体的数据库连接参

数,这些参数描述了 PowerBuilder 通过接口与数据库建立连接时所有的重要信息。在

Connection 属性页中,输入描述文件名,选择数据源并输入用户名和密码。在填完选项以

后,按 Preview 标签页,出现如图 2-7 所示的预览窗口,可以查看系统自动生成的连接语

句。

对不同的数据库管理系统,定义数据库配置时显示的对话框稍有不同,连接参数也不

一样。虽然如此,不管配置哪个数据库,配置 Database Profile Setup-...对话框都有共同的

5 个标签页:Connection、System、Transaction、Syntax、Preview。其中前 4 个标签页分

第 2 章 使用 Pow erBuilder操纵数据库

— 19 —

别指定某个方面的连接参数,而 Preview 标签页显示了相应设置在应用程序脚本中的表示

方式。数据库专用的标签页主要用于帮助开发人员设置 DBParm 参数。

图 2-7 Preview 标签页

PowerBuilder 利用创建好的数据库描述文件与数据库相连。方法是选中图 2-5 中的描

述文件名,单击 Connect 按钮,便建立了与数据库的连接。

2.2.3.2 数据库描述文件的导入与导出

数据库描述文件还可以导入与导出。所谓导出,指的是将某个或全部数据库描述文件

转换成.ini 格式文件,而后在必要的时候再把这个文件导入到 Database Profiles 窗口中的

树状列表中。这样做的好处是,通过 ini 文件很容易进行描述文件的创建,同时能在不同

的平台上很方便地移植数据库描述文件,而不必重复复杂的配置过程。

1. 导入数据库描述文件

导入数据库描述文件的步骤如下。

(1) 在 Database Profiles 窗口中单击某个数据库接口,使其变为高亮区。单击鼠标右

键,在弹出菜单中选择 Import Profile 项,如图 2-8 所示。

(2) 从打开的 Select Profile File 对话框中选择要导入的描述文件(.ini),单击“打开”

按钮。

(3) 在如图 2-9 所示的导入描述文件的对话框中选择要导入的描述文件,然后单击

OK 按钮。这些描述文件被写入 PB.ini 文件中,如果一个描述文件已经存在,系统将提示

是否需要覆盖它。

Pow erBuilder9.0 基础应用与系统开发

— 20 —

图 2-8 弹出菜单

2. 导出数据库描述文件

导出数据库描述文件的步骤如下。

(1) 在图 2-8 所示的 Database Profiles 窗口中单击某个要导出的数据库接口,使其变

为高亮区。单击鼠标右键,在弹出菜单中选择 Export Profile 项。

图 2-9 选择导入描述文件

(2) 从 Export Profile(导出描述文件)对话框中选择要导出的描述文件,如图 2-10 所

示,然后单击 OK 按钮。

图 2-10 选择导出描述文件

(3) 在弹出的对话框中选择保存文件的路径和名称,单击“保存”按钮。

第 2 章 使用 Pow erBuilder操纵数据库

— 21 —

2.2.4 ODBC 接口连接实例

下面以创建 Sybase Server Anywhere 的 ODBC 连接为例,说明在 PowerBuilder 中如何

连接数据库。

2.2.4.1 安装 ODBC 驱动程序

要想通过 ODBC 接口访问某种数据库,必须安装这种数据库的 ODBC 驱动程序。有

两种途径可以获得 ODBC 驱动程序。

(1) 从 Sybase 公司获得。在 PowerBuilder 安装盘中带有一些 ODBC 驱动程序。

(2) 从数据库厂商或其他 ODBC 驱动程序提供商处获得。大多数数据库厂商都提供访

问自己数据库的 ODBC 驱动程序。

对于一些网络数据库,安装 ODBC 驱动程序的同时还必须安装和配置相关数据库服

务器的客户端软件。例如,访问 Sybase 还需安装它的客户端软件 Open Client。这是因为

ODBC 驱动程序最终还必须通过数据库服务器的客户端软件与服务器进行通信。

2.2.4.2 准备 ODBC 数据源

ODBC 数据源是指要连接的数据库、相关网络软件、操作系统软件等支持环境的总

称。每个 ODBC 数据源都定义了一系列指向某个特定数据库的参数,通过这个 ODBC 数

据源,应用程序即可访问指定的数据库。下面给出定义数据源 ds_client(指向数据库

db_client.db)的步骤。

(1) 启动 PowerBuilder,在工具栏上单击 DB Profile 图标,打开 Database Profiles 画

板。

(2) 在 Database Profiles 画板中双击 ODB ODBC 节点下的 Utilities 节点下的 ODBC

Administrator 项,弹出如图 2-11 所示的“ODBC 数据源管理器”对话框。

图 2-11 ODBC 数据源管理器

Pow erBuilder9.0 基础应用与系统开发

— 22 —

(3) 在“ODBC 数据源管理器”中单击“添加”按钮,弹出如图 2-12 所示的“创建

新数据源”对话框,选择驱动程序为 Adaptive Server Anywhere7.0。

图 2-12 “创建新数据源”对话框

(4) 单击“完成”按钮,弹出如图 2-13 所示的配置 ODBC 的对话框。

图 2-13 配置 ODBC

(5) 在 ODBC 页中,设置数据源名称,这里将数据源名称设为 ds_client。

(6) 在如图 2-14 所示的配置对话框的 Login 标签页中,设置用户名和口令。这里将用

户名设为 dba,口令为设 sql。

(7) 在如图 2-15 所示的对话框的 Database 标签页中,设置服务器的名称和数据库文

件,这里,将服务器名设为“销售数据库”,并指定数据库文件为 G:\GB_CLIENT.DB。

(8) 单击“确定”按钮,关闭该配置对话框和 ODBC 数据源管理器,返回 Database

Profile 画板。至此,ODBC 数据源 ds_client 定义工作就全部完成了。

第 2 章 使用 Pow erBuilder操纵数据库

— 23 —

图 2-14 Login 标签页

上述定义工作同样也可在控制面板中的 ODBC 数据源中完成。双击控制面板中的

ODBC 数据源图标,弹出如图 2-11 所示的 ODBC 数据源管理器,其余步骤同上述过程。

图 2-15 Database 标签页

对于不同的数据库和不同的 ODBC 驱动程序,定义 ODBC 数据源的方法不尽相同。

具体定义方法可以参阅 ODBC 驱动程序厂商提供的帮助文件。

2.2.4.3 定义数据库描述文件 Database Profile

定义好数据源后,要使用这些数据源,还需要配置数据库描述文件。步骤如下。

Pow erBuilder9.0 基础应用与系统开发

— 24 —

(1) 在 Database Profiles 画板中选中 ODB ODBC 节点,然后单击 New 按钮弹出

Database Profile Setup 对话框。在此对话框中指定要连接的 ODBC 数据源、设置 Profile 文

件名以及用户名和口令,具体设置如图 2-6 所示。如果在定义 ODBC 数据源时已指定了

用户名和口令,此处可以不再填写用户名和口令。

(2) 单击 OK 按钮,关闭对话框,返回 Database Profiles 画板。

到此为止,一个访问 Sybase Adaptive Server Anywhere 数据库 DB _CLIENT.DB 的

ODBC 连接就完全定义好了。此时,可在 Database Profile 画板中选中数据库描述文件名

sale,然后单击 Connect 按钮即可连接到数据库 db_client.db。

注意

用 PowerBuilder 9.0 的数据库画板创建本地 Sybase Server Anywhere 数据库时,在数据

库创建完成后系统会自动创建数据源和描述文件,并自动连上数据库。数据源和描述文件

的名称默认为与数据库同名。

2.2.4.4 改变当前数据库

在同一时间内,PowerBuilder 开发环境只能连接到一个数据库,这个数据库称为当前

数据库。当我们想连接到其他数据库时,可使用数据库配置画板(DB Profile)改变当前数

据库,步骤如下。

(1) 单击工具栏的 DB Profile 图标,弹出 Database Profiles 对话框。

(2) 从中选择需另行连接的数据库描述文件名。

(3) 单击 Connect 按钮便连接到新的数据库。

2.3 操作数据库表

表是数据库的重要组成部分。关系数据库的表可以表达成二维形式。表的列定义了表

的结构,每一列定义一个字段。存放在表中的数据用行表示,一行数据表达一条记录。对

数据库表的操作主要包括:创建表、定义主键、定义外键、修改表、删除表、删除主键,

以及对数据库表中的数据进行操作等。

2.3.1 创建表

在使用数据库表之前需要首先创建表。创建的途径有两条:利用数据库管理系统本身

提供的工具创建表;通过 PowerBuilder 的图形界面来创建表。本节只介绍第 2 种途径。

具体步骤如下。

1. 定义表中每一列字段的属性

单击工具栏上 Database 图标,打开数据库画板。在 Objects 窗口中已连接的当前数据

库的 Tables 文件夹上单击鼠标右键,在弹出菜单上选择 New Table⋯菜单项或在工具栏上

单击 Create Table 图标,在 Columns 窗口出现如图 2-16 所示的定义新表各列的列表框。

第 2 章 使用 Pow erBuilder操纵数据库

— 25 —

图 2-16 Colums 窗口

图 2-16 中的列表框要求用户为每一个字段定义六项属性。

定义一个字段属性的方法如下。

(1) 在 Column Name(字段名)栏输入字段名,注意它在表中必须是惟一的,最好具有

一定的描述性,即表达一定的意义。

(2) 在 Data Type(数据类型)栏选择输入该字段的数据类型。使用下拉列表框可以看到

所有可能的选项。

(3) 在 Width(参数)栏输入字段的宽度,该项只对有些类型是必要的。在 Dec 下输入

小数位数值。当数据类型为 numeric 时,该项才有必要输入,它表示小数点右边能有的最大位

数。

(4) 在 Null(是否允许为空)栏单击下拉式列表框,选择该字段是否可以为空。

(5) 在 Default 栏的下拉式列表框中选择一个默认值。它表示如果用户输入记录中的

数据时,没有向该字段输入任何值,那么该字段就取此默认值。按 Enter 键,按上述同样

的方法定义下一个字段的具体信息。

2. 在属性窗口中指定各列的扩展属性

如果要对某一列进行更为复杂的设定,则可以在属性窗口中指定各列的扩展属性。需

要注意的是,扩展属性并不作为表的一部分存储,而是存储在 PowerBuilder 的系统表

中。设置列的扩展属性并不是必需的,但是扩展属性可以令一个表更加完整。

3. 保存

设置完各列后,单击工具栏上的 Save 按钮,弹出 Create New Table 对话框,在对话

框中的 Table Name 编辑框中输入表的名称,单击 OK 按钮,保存该新表。

2.3.2 修改表

创建完表以后,如果发现某些地方不合适,则可以修改表的定义。步骤如下。

(1) 在数据库画板的对象子窗口中,右键单击欲修改的表,在弹出菜单中选择 Alert

Table 项。

Pow erBuilder9.0 基础应用与系统开发

— 26 —

(2) 列子窗口中出现表修改工作窗口,该窗口与创建新表工作窗口十分相似,但其中

有些选项被限制使用或不能使用。例如,不能完成下列修改:改变列的类型、改变列的

Null 状态、缩小列的宽度、在表的中间添加新列。一般来说,不限制使用的选项都是可

修改项。

(3) 要想在最后一列的后面增加新列,那么首先将插入点移动到窗口的最后一列上,

然后按 Enter 键,在新增的空白行中输入列的定义。

(4) 若要删除某列,先从表修改工作窗口中选中该列对应的行。在最左端右击,在弹

出菜单中选择 Delete Column,可删除该列。

单击工具栏上的保存图标,保存所做的修改。

2.3.3 定义表的属性

建立完一个表后,还需为它指定属性。这可以在如图 2-17 所示的属性子窗口中完

成。可以使用以下两种方法来打开属性子窗口中的表属性区。

图 2-17 表的属性区

(1) 选择要设置属性的表,单击专用工具栏上的 Properties 图标。

(2) 选择要设置属性的表并右击,在弹出菜单中选择 Properties 项。

表的属性的设置主要是指表的默认字体的设置。表的字体是作为数据窗口的默认字体

使用的。在表的属性中,设置的默认字体将覆盖应用对象属性中设置的默认字体。设置步

骤如下。

(1) 打开所选表的属性区。

(2) 在 General 标签的 Comments 栏中可输入一些注释,这些注释将出现在数据库画

板工作区的表的图示中。另外在 General 标签页中也能看到该表属于哪个用户。

(3) 单击 Data Font 标签,在该标签页中,设置数据窗口中数据(列)的默认显示字体。

(4) 单击 Heading Font 标签,在该标签页中,设置数据窗口中数据列标题的默认显示

字体。

(5) 单击 Label Font 标签,在该标签页中,设置自由风格数据窗口对象中列标签的默

认显示字体。

第 2 章 使用 Pow erBuilder操纵数据库

— 27 —

2.3.4 主键

表的主键是能够惟一标识每一行数据的一个列或多个列的集合。也就是说,在表中任

意两行的主键键值都不相同。在选择作为主键的列时,必须认真考虑,既要使它能够惟一

标识每一行,又要使它不包括不必要的列。在 PowerBuilder 中,必须为表定义主键,否

则,不能利用 PowerBuilder 的图形界面进行某些数据操作,比如数据插入等。

定义表的主键的步骤如下。

(1) 在数据库画板的对象子窗口中,在欲建立主键的表上右击,在弹出菜单中选择

New Primary Key 项,或选中表后,单击工具栏上的 Create new primary key for selected table

图标,在属性子窗口中出现如图 2-18 所示的主键区的对话框。

图 2-18 定义主键

(2) 在 Columns 栏中单击作为主键的列,可以通过鼠标的拖动操作改变主键列的先后

位置。

(3) 选择好主键列后,单击工具栏上的“保存”按钮,保存设置。

2.3.5 定义外键

外键是表中的一个或多个列,这些列与其他表中作为主键的列相对应。在关系数据库

中,可利用外键连接多个表,建立数据库的参照完整性。通过主键和外键,可以利用关系

数据库系统本身保证数据输入的有效性,避免烦琐的编程。在一个表上可以根据需要定义

任意多个外键。

定义外键的步骤如下。

(1) 在数据库画板的 Objects 窗口中,在欲建立外键的表上右击,在弹出菜单中选择

New Foreign Key 项,或选中表后,单击工具栏上的 Create Foreign Key 图标,在属性子窗

口中出现如图 2-19 所示的外键区。

(2) 在 General 页中,在 Foreign Key 栏中输入外键的名称,在 Columns 列表框中单击

作为外键的列。

Pow erBuilder9.0 基础应用与系统开发

— 28 —

图 2-19 创建外键

(3) 如图 2-20 的 Primary Key 页中选择参照表(主表),该参照表中的主键列必须与定

义的外键列相同。所选表的主键出现在 Columns 列表中。

图 2-20 选择参照表

(4) 在 Rules 页中,设置具有外键联系的表之间的规则,该页界面如图 2-21 所示。

图 2-21 定义主表与从表的数据关系

该页只有一栏,题为 On Delete of Primary Table Row,该栏的选项指定主表行的处理

规则,其选项如下。

第 2 章 使用 Pow erBuilder操纵数据库

— 29 —

(1) Disallow if Dependent Rows Exist[RESTRICT]:如果从表中存在相应的数据行,则

不允许删除主表中的行。

(2) Delete any Dependent Rows[CASCADE]:删除主表中的行时,同时删除从表中相

应的数据行。

(3) Set Dependent Columns to NULL[SET NULL]:删除主表中的行时,同时把从表中

相应行的外键列的值设置为 NULL。

设置好上述选项后,单击工具栏上的“保存”按钮,保存对外键的定义。

2.3.6 删除表、主键、外键

在开发数据库应用系统的过程中,不可避免地会产生一些不再使用的表,这些表都可

以在 PowerBuilder 开发环境中随时删除。如果主键或外键定义得不合适,同样能够随时

删除。

删除表的步骤如下。

(1) 在数据库画板的对象子窗口中,右键单击选定要删除的表。

(2) 在弹出菜单中选择 Drop Table 项,系统显示 Database 对话框,询问你是否真的要

删除选定的表,单击“是”按钮,该表就被删除了。

在数据库画板中可使用相似的步骤删除表的主键、外键。步骤如下。

(1) 数据库画板的对象子窗口中,右键单击代表要删除主键、外键的图标。

(2) 在弹出菜单中选择 Drop Primary Key 或 Drop Foreign Key,系统询问是否真的要

删除所选择的主键或外键,单击 Yes 按钮,所选的主键或外键被删除。

2.3.7 数据操作

建立了表之后,就可在表中输入数据了。输入数据的方法有两种:一种是直接输入,

PowerBuilder 提供了 3 种格式的工作区用来输入数据,分别为 Grid(表格格式)、Tabular(列

表格式)和 Freeform(自由格式);另一种方法是从数据文件里将数据插入表中。第 1 种方法

简单明了,第 2 种方法适合于批量数据的加载。此外,在开发环境中也可以将表中的数据

导出到文件中。PowerBuilder 支持十多种类型的导出文件格式,可以根据需要灵活选择。

下面介绍在 PowerBuilder 开发环境中浏览、输入、输出表中数据的方法。

在数据库画板的对象子窗口中,选择要进行数据操作的表并右击,在弹出菜单中选择

Edit Data 项,如图 2-22 所示。

图 2-22 操作数据

Pow erBuilder9.0 基础应用与系统开发

— 30 —

1. 工具栏

如果是刚建立的表,则工作区中没有任何数据,只有每一列的标题(对于 Grid 表),同

时工具栏上增加了一排针对数据操作的专用工具栏,图标说明见表 2-2。

表 2-2 针对数据操作的专用工具栏说明

图 标 名 称 说 明

Retrieve 检索表中的数据

Save Changes 保存表中的数据

Insert Row 插入一行

Delete Row 删除当前行

First 翻到第 1 页

Prior 翻到前一页

Next 翻到后一页

Last 翻到最后一页

Print 打印表中的数据

在表数据输出区(Output)中右击,在弹出菜单中选择 First Page、Last Page、Prior

Page、Next Page 中的一项或单击工具栏上的 First、Prior、Next、Last 图标之一来翻页,

查看当前页中被遮挡的部分,进行数据浏览。

2. 设置数据排序方式

当我们希望以某种次序查看数据时,可以指定数据的排序方式,步骤如下。

(1) 从菜单栏中选择 Rows|Sort 子菜单项,弹出如图 2-23 所示的 Specify Sort Columns

对话框。

图 2-23 Specify Sort Columns 对话框

(2) 将左边列表框中的某列拖放到右边列表框中,出现在右边列表框中列是排序列。

(3) 指定排序方式,选中 Ascending 复选框时按升序排列,否则按降序排列。

(4) 增加其他的排序列和排序方式。

(5) 单击 OK 按钮。

第 2 章 使用 Pow erBuilder操纵数据库

— 31 —

3. 指定过滤条件

当表中的数据很多,而我们只想要查阅一部分符合需要的数据时,可以指定过滤条

件,步骤如下。

(1) 从菜单栏中选择 Rows Filter 子菜单项,弹出如图 2-24 所示的 Specify Filter 对话框。

图 2-24 Specify Filter 对话框

(2) 输入过滤条件。可以直接输入,也可以通过单击函数、列名以及操作符来构造过

滤条件。

(3) 单击 Verify 按钮验证过滤条件是否有效。

(4) 单击 OK 按钮,关闭对话框,满足条件的数据被显示出来。

4. 编辑和显示数据

当想插入一行数据时,如果是在表格的末尾增加,只要按 Enter 键,就可以增加一空

行,然后往里面输入数据就可以了。

如果是在某一行的前面插入一行,可先把光标移到指定的行中,然后单击专用工具栏

上的 Insert Row 图标或单击鼠标右键在弹出菜单中选择 Insert Row 项,便在指定行的前面

插入一空行,然后就可以往里面输入数据了。

要删除某一行数据,可先把光标移到指定的行中,然后单击专用工具栏上的 Delete

Row 图标,或单击鼠标右键在弹出菜单中选择 Delete Row 项,便将指定的一行数据删

除。

要修改某个数据项的内容时,通过单击将插入点移动到该数据项上,修改该数据项即

可。

完成所有修改后,单击数据库画板下的数据操作专用工具栏上的保存图标,所做的修

改保存到数据库中。不想保存所做的修改时,单击数据库画板下的数据操作专用工具栏上

的关闭图标,系统询问是否保存修改时,选择“否”按钮即可。

当希望显示该表的最新数据时,单击数据库画板下的数据操作专用工具栏上的检索图

标。但要注意的是,如果刚刚在数据库画板下的数据操作专用工具栏修改了某些数据但尚

未将其保存到数据库中,那么单击检索图标将丢失所做的修改,PowerBuilder 并不给出任

何提示。

Pow erBuilder9.0 基础应用与系统开发

— 32 —

5. 导出和导入数据

在数据库画板下的数据操作专用工具栏中,既可以把表中数据按多种格式导出到磁盘

文件供其他软件使用,也能够把多种格式磁盘文件中的数据导入到数据库表中。导出数据

的步骤如下。

(1) 从菜单栏中选择 File Save Rows As 子菜单项,弹出 Save As 对话框。

(2) 在保存类型的列表框中选择数据保存格式。

(3) 选择文件夹后在“文件名”编辑框中键入文件名称。

(4) 单击“保存”按钮。

将文件中的数据导入表中的步骤如下。

(1) 从菜单栏中选择 Rows Import 子菜单项,弹出 Select Import File 对话框,选择用

制表符分隔的文本文件类型或 Dbase 文件类型,然后选择数据文件。

(2) 单击“打开”按钮,数据被插入到数据库画板下的数据操作专用工具栏中,出错

时系统会指出错误。

(3) 单击数据库画板下的数据操作专用工具栏上的保存图标,数据被保存到表中。

将当前数据库画板下的数据操作专用工具栏中的数据打印输出也很简单,只要单击数

据库画板下的数据操作专用工具栏上的打印图标或执行 File Print 菜单命令即可。如果想

在打印之前预览一下打印效果,可执行 File Print Preview 菜单命令。

2.4 使用视图

视图是数据库应用系统中一个十分重要的组成部分,是查找数据库中一个或多个表中

数据的一种方法。可以把视图当做一种虚表,并且能够像表一样访问和使用视图。然而,

数据库中并不真正存储视图的物理结构和数据。视图中的数据行和列是来自于基本表,并

且是由定义视图的查询产生的。当用户修改在视图中看到的数据时,实际是在修改基本表

的数据,反过来基本表中数据的改变也会自动地反映到由它们导出的视图中。通过定义视

图,可以屏蔽表中的某些信息,这样当用户访问该视图时,就只能访问他所需的数据,而

看不到他不想看或不应该看到的数据,做到提高效率和信息保密。

2.4.1 创建视图

在数据库画板的对象子窗口中,在欲建立视图的数据库的 View 节点处右击,在弹出菜

单中选择 New View 项或在工具栏上单击 Create View 图标,弹出 Select Table 对话框,在该

对话框中选择所要建立视图的表名,单击 Open 按钮,进入如图 2-25 所示的视图工作区。

整个视图工作区分为两个部分,上部为表格操作区,下部为辅助操作区。

1. 表格操作区

在该区中,用户可以根据需要在视图中添加和关闭表。需要添加表时,把鼠标移到表

格操作区的空白处右击,在弹出菜单中选择 Select Tables,弹出 Select Tables 对话框,可

以在其中选择需要的表。关闭不需要的表时,只需要把鼠标移到该表的标题栏上右击。在

弹出菜单中选择 Close 项即可。选择好表后,用户可以在表中选择建立视图所需要的列。

第 2 章 使用 Pow erBuilder操纵数据库

— 33 —

图 2-25 视图工作区

2. 辅助操作区

视图的辅助操作区有 6 个标签页面:Sort、Where、Group、Having、Compute 和

Syntax。Sort 页用来对位于其中的列进行排序;Where 页用来指定视图操作数据的限定条

件;Group 页用来对表格中的特定列进行分组;Having 页是对 Group By 操作的进一步限

定,用来筛选分组后的数据;Compute 页的作用是对表中的某些列进行计算而产生计算

列;Syntax 是对建立视图过程的 SQL 语句说明。

建立完视图后,单击工具栏的 Return 图标,弹出 Save View Definition 对话框。在该

对话框中输入视图的名称,然后单击 Create 按钮或 Log Only 按钮。

如果单击 Create 按钮,PowerBuilder 会把建立视图的 SQL 语句提交给数据库管理系

统;如果单击 Log Only 按钮,PowerBuilder 会把建立视图的 SQL 语句写入到日志文件

中,但不会提交给数据库管理系统。

图 2-26 所示视图是由图 2-25 的表导出的。从图 2-26 可以看出,视图显示的内容结构

与数据工作区中的表一样,只不过它是由多个表中的部分字段组成的。

图 2-26 视图数据 图 2-27 视图的 SQL 语句描述

建立视图时 PowerBuilder 会自动生成一个相应的 SQL 语句。在视图的属性区(查看视

图属性的方法与查看表属性的方法相同)中 General 页的 Definition 栏可以看到这个语句,

如图 2-27 所示。可以把这个 SQL 语句应用到程序中,得到所需要的结果。

Pow erBuilder9.0 基础应用与系统开发

— 34 —

在视图图标上右击,在弹出菜单中选择 Edit Data/Grid 项可以看到该视图的具体内

容。

2.4.2 删除视图

当不再使用视图时,可以将其删除。方法为:在欲删除视图的图标上右击,在弹出菜

单上选择 Drop View 项,当系统询问是否要删除时,选择“是”按钮确认,所选的视图被

删除。

另外需要强调的是,视图不能修改。如果要修改视图,需要先删掉该视图,然后重新

进行定义。

2.5 存储过程和触发器

存储过程是 SQL 语句和可以快速执行的流控制语言的可重用集合。它存储在数据库中,

可以从应用程序中调用执行,且允许使用用户说明的变量。用户可以用存储过程来完成经常

实施的任务。触发器是存储过程的特殊形式,它可以用来保证数据的一致性和完整性。

2.5.1 存储过程

可以通过 SQL 语句创建存储过程,但由于 DBMS 的不同,存储过程的语法也不尽相

同。

在 PowerBuilder 本地数据库 Sybase Server Anywhere 中创建存储过程的语句的语法如

下:

CREATE PROCEDURE [owner .] procedure_name

.. . [ [ ( ) @parameter_name data-type [ = default ] [ OUTPUT ], ...

[ ] ] ]

... [ WITH RECOMPILE ]

.. . AS

... statement-list

其中 parameter 包括参数模式(IN、OUT 和 INOUT)、参数名和参数数据类型。用户只

能在当前数据库中创建存储过程。WITH RECOMPILE 是指该存储过程将在每次执行时都

重新编译。

在数据库中创建好存储过程后,在使用之前还需在 PowerScript 程序中定义存储过

程,语法如下:

DECLARE ProcedureName PROCEDURE FOR

StoredProcedureName

@Param1=Value1,@Param2=Value2,� {USING TransactionObject};

其中,ProcedureName 为 PowerBuilder 中任意合法的过程名称;StoredProcedureName

为数据库中存储过程的名称;@Paramn、Valuen 是在存储过程以及合法的 PowerBuilder

表达式中定义的参数名或数值;TransactionObject 指该过程的事务对象名(这个子句只用于

第 2 章 使用 Pow erBuilder操纵数据库

— 35 —

SQLCA 以外的事物对象,为特定的事务对象说明一个过程)。这是个不可运行指令,与变

量说明类似。

下面的例子说明如何创建和定义存储过程:假设某个 Sybase Adaptive Server 服务器

下某个数据库中有一个存储过程名为 proc_sysbase,该过程接受一个长方形长和宽的值,

输出长方形面积:

CREATE PROCEDURE proc_Sybase

@length decimal(2,1),

@width decimal(2,1),

@area decimal(3,2)out

as

begin

select @area=@length*@width

end;

在 PowerScript 程序中可这样定义该存储过程:

decimal len,wid,area

DECLARE proc_area PROCEDURE FOR proc_Sybase

@length=:len,

@width=:wid,

@area=:area out

USING TRAN_OBJECT;

其中,TRAN_OBJECT 指向 Sybase Adaptive Server 下的某个库。

执行存储过程的语法为:

EXECUTE ProcedureName;

其中,ProcedureName 为准备运行的存储过程的名,该存储过程必须是已经定义好的。

例如,下面的语句用来执行存储过程 proc_area:

EXECUTE proc_area;

执行完该存储过程后,服务器会把返回的数据放在存储过程的结果集中。

关闭存储过程的语法为:

CLOSE ProcedureName;

其中,ProcedureName 是要关闭的存储过程。在执行该语句时,指定的存储过程必须

已经执行。只有通过 CLOSE 语句才能将存储过程返回的结果集关闭。如果没有返回结果

集,则 PowerBuilder 会自动关闭存储过程。

例如,下面的语句用来关闭存储过程 proc_area:

CLOSE proc_area;

2.5.2 触发器

触发器(Trigger)是一类专门的存储过程,它用于一种或多种数据修改操作 UPDATE、

Pow erBuilder9.0 基础应用与系统开发

— 36 —

INSERT 或 DELETE,在完成数据修改之后立即执行。触发器和触发它的语句被当做一个

事务,可以从触发器中回滚,如果系统检测到错误,整个事务自动回滚。

触发器是在特定表上定义的。只有用户表的所有者才有权创建触发器和从用户表中删

除触发器,该权限不能转授。

创建触发器的语法如下:

CREATE TRIGGER trigger-name trigger-time trigger-event[,trigger-event,�] ⋯[ORDER integer] ON table-name ⋯[REFERENCING[OLD AS old-name] [NEW AS new-name]] [REMOTE AS remote-name]] ⋯[FOR EACH{ROW|STATEMENT}] ⋯[WHEN(search-condition)] ⋯[IF UPDATE(column-name) THEN ⋯[{AND|OR}UPDATE(column-name)]�] �compound-statement ⋯[ELSEIF UPDATE(column-name)THEN ⋯[{AND|OR}UPDATE(column-name)]� �compound-statement ⋯ END IF]]

其中,trigger-name 为触发器名称,table-name 是表名,column-name 是列名。表名是

该触发器发作的对象。FOR 子句用来设定该触发器的启动时机。如果针对表的某些列被

更新才启动触发器的话,必须使用 IF UPDATE 子句。可以利用 AND 和 OR 逻辑运算符

组合设定多个列作为启动触发器的条件。

应用触发器的规则如下。

(1) 一张表最多只能有 3 种触发器操作:UPDATE、INSERT 和 DELETE,单个触发

器可以处理全部动作。

(2) 每种触发器只能作用于一张表。

(3) 虽然触发器可以参照视图或临时表,但不能在视图或临时表上建立触发器。

(4) 触发器不能包括向用户返回结果的 SELECT 语句,因为它无法为客户应用程序捕

获结果。

下面的例子为在销售数据库中的用户表 t_clientinfo 上建立一个 UPDATE 触发器,当

更改列 cno 时触发。代码如下。

CREATE TRIGGER clientinfo_update ON t_clientinfo

FOR UPDATE AS IF UPDATE(cno) BEGIN RAISERROR(�Transaction cann�t be Processed�,10,1) ROLLBACK TRANSACTION END END IF

第 2 章 使用 Pow erBuilder操纵数据库

— 37 —

删除已存在的触发器的语法为:

DROP PROCEDURE 触发器名#1,触发器名#2,⋯触发器名#n。

2.6 使用事务对象

事务是关系数据库管理系统提供的一种机制,它用于使一个或多个 SQL 语句被当成

单独的工作单元来处理。

通常,一个应用程序从数据库中读取数据,并且将结果存放在内存之中。所有的用户

交互发生时将数据放置在内存,并且对数据库没有形成修改的时候。当完成用户的交互之

后,应用程序将所有的修改动作发布给数据库,并且提交给事务。

事务的特点是要么工作单元内所有的操作都成功,要么都失败。如果在修改数据库的

时候,有错误产生,应用程序会发布一个 ROLLBACK 命令,并且,所有的对数据库进行

的修改动作都被取消。这对关系数据库系统中数据的完整性、一致性非常重要。

2.6.1 事务对象简介

一个事务对象是一个不可见的 PowerBuilder 对象,提供用户的应用程序和数据库管

理系统之间的通信,事务对象的功能如图 2-28 所示。用户保存在事务对象中的信息被用

来连接数据库,并且接收来自数据库的错误和状态信息。

PowerBuilder 应用程序

事务对象

状态信息

连接参数

DBMS

图 2-28 事务对象功能示意

每个事务对象都有 16 个属性,其中 10 个用于提供数据库管理系统 DBMS 所需的连

接信息,6 个用于返回每条 SQL 语句运行成功、失败的信息。表 2-3 描述了事务对象的这

些属性。

表 2-3 事务对象的属性

属 性 数据类型 描 述 DBMS String 数据库厂商名称

Database String 数据库名称

UserID String 与数据库连接的用户 ID

DBPass String 与数据库连接的用户口令

Lock String 事务的独立级别

LogID String 登录数据库服务器时所需的注册 ID

LogPass String 登录数据库服务器时所需的口令

ServerName String 数据库所在的服务器名

AutoCommit Boolean 自动提交指示(只用于 Sybase)

TRUE:数据库更新后自动提交,所有的 SQL 操作都在事务之中处理

FASLE:数据库更新之后不自动提交

Pow erBuilder9.0 基础应用与系统开发

— 38 —

(续表)

属 性 数据类型 描 述 DBParm String DBMS 指定的设置连接参数

SQLCode Long

当前 SQL 操作成功或失败的代码

0:表示成功

100:表示执行 select 语句时查找不到符合条件的数据

-1:表示 SQL 操做出错

用 SQLErrText 或 SQLDBCode 可得到错误信息

SQLNRows Long SQL 语句作用到的行数,在不同的 DBMS 中可能会不同

SQLDBCode Long 数据库厂商提供的错误代码

SQLErrText String 数据库厂商提供的错误信息

SQLReturnData String 特定 DBMS 的返回信息

ClassDefinetion Powerobject 一个 Powerobject 对象类型的对象,用来提供 Powerobject 对象的类定义

在访问一个数据库之前,必须设置事务对象,PowerBuilder 根据事务对象提供的连接

数据库的反馈信息,做出是否允许当前应用与数据库进行通信的决定。

2.6.2 SQLCA 全局事务对象

事 务 对 象 指 向 要 连 接 的 数 据 库 , 由 于 多 数 应 用 系 统 只 与 一 个 数 据 库 连 接 ,

PowerBuilder 提供了一个默认的全局事务对象型变量 SQLCA,它表示 SQL 语句的通信区

域(SQL Communication Area)。当然,根据需要也可创建其他的事务对象。在大多数情况

下 SQLCA 是惟一需要的事务对象。

SQLCA 是系统用来处理应用程序与数据库之间的通信事务对象。在执行一个应用程

序之前,必须先设置 SQLCA 属性。

下面的语句用于对 SQLCA 的属性进行设置。

SQLCA.DBMS = ProfileString(�PB.INI�, �Database�, �DBMS�, � �) SQLCA.Database = ProfileString(�PB.INI�, �Database�, �DataBase�, � �) SQLCA.LogID = ProfileString(�PB.INI�,�Database�, �LogID�, � �) SQLCA.LogPass = ProfileString(�PB.INI�, �Database�, �LogPassword�, � �) SQLCA.ServerName = ProfileString(�PB.INI�,�Database�,�ServerName�, � �) SQLCA.UserID = ProfileString(�PB.INI�, �Database�, �UserID�, � �) SQLCA.DBPass = ProfileString(�PB.INI�, �Database�, �DBPass�, � �) SQLCA.Lock = ProfileString(�PB.INI�, �Database�, � Lock�, � �) SQLCA.DbParm = ProfileString(�PB.INI�, �Database�, �DbParm�, � �)

上面的设置属性语句是 PowerBuilder 提供的 PB.INI 文件中的 DataBase 部分,

PowerBuilder 会在一个应用程序中完成对这部分的赋值。ProfileString 函数的作用是把最

后一次连接数据库管理系统属性的值赋给本次应用程序事务对象。当改变当前应用程序与

数据库的连接时,只要重新给 SQLCA 赋予新数据库的各种属性值就可以了。

我们经常使用事务对象的连接语句来进行应用程序与数据库的连接。通常,

PowerBuilder 的数据库应用程序在初始化时完成对某个数据库的连接,即在对事务对象进

行属性设置后,可使用下面的语法连接数据库:

CONNECT {USING TransactionObject}

其中 TransactionObject 是包含准备连接数据库的连接信息的事务对象名,该子句在连

接的事务对象不是默认的 SQLCA 时必须使用。当事务对象是 SQLCA 时省略。

第 2 章 使用 Pow erBuilder操纵数据库

— 39 —

连接上数据库后,在一个单元中的处理工作应该作为一个事务提交,可使用

COMMIT 语句来提交这次事务并且开始一个新的事务。如果提交事务失败,则应该使用

ROLLBACK 命令返回在数据库中进行的一切操作,终止这次事务并且开始一个新的事

务。

提交事务的语法为:

COMMIT {USING TransactionObject}。

取消应用程序对数据库的修改操作的语法为:

ROLLBACK {USING TransactionObject}。

下面是提交事务和返回事务(使用 SQLCA)的例子:

if dw_clientinfo.UPDATE>0 then

COMMIT。

MessageBox(�OK�,�UPDATE DATABASE SUCCESS�) else ROLLBACK。 MessageBox(�ERROR�,�UPDATE DATABASE FAILURE�) end if

在应用程序处理完数据后,应该断开与数据库的连接,终止这次事务的操作。可使用

DISCONNECT 语句来结束当前数据库访问。

断开与指定数据库的连接的语法为:

DISCONNECT {USING TransactionObject}。

2.6.3 自定义事务对象

如果要同时操作多个数据库,则应使用多个事务对象。建立每一个与数据库的连接都

需要一个事务对象,除 SQLCA 外的那些事务对象必须经过说明和创建,而当它们不再需

要时,必须删除。

说明事务对象的语法为:

Transaction TransactionObject

其中,TransactionObject 为事务对象名。为了应用事务对象,还必须创建事务对象,

创建事务对象的语法为:

TransactionObject = Create Transaction

自定义事务对象的使用方法与默认的事务对象 SQLCA 的使用方法相同,不再叙述。

下面的例子用默认的事务对象 SQLCA 连接到 SQL SERVER 数据库,用另外创建的

事务对象 SQLTrans 连接到 SYBASE 数据库。

Transaction SQLTrans SQLCA.DBMS = �MSS Microsoft SQL Server 6.x�

Pow erBuilder9.0 基础应用与系统开发

— 40 —

SQLCA.Logid = �HEAVEN� SQLCA.Logpass = �GHOST� SQLCA.servername = �SQLSERVER� CONNECT。 SQLTrans = CREATE TRANSACTION SQLTrans.DBMS =�SYC Adaptive Server Enterprise� SQLTrans.Logid =�HELLO� SQLTrans.Logpass = �ANGLE� SQLTrans. servername = �SYBASE� CONNECT USING SQLTrans。 ⋯ ⋯ DISCONNECT;//断开到 SQL SERVER 的连接 DISCONNEDT USING SQLTrans;//断开到 SYBASE 的连接 DESTROY SQLTrans;//当不再需要 SQLTrans 时,予以删除

2.7 创建和应用数据库的实例

2.7.1 创建数据库

首先在硬盘的 E 分区下建立一个 PBExample 文件夹,然后在此文件夹下创建一个工

作区 pbapp.pbw,进而创建库文件 pbapp.pbl 和应用程序对象 pbapp。

单击 PowerBuilder 工具栏的数据库画板图标,右击 ODB ODBC 节点,选择 New

Profile,并对新建立的数据库进行命名。为了简单起见,在此数据库中只建立一个表

Student(Sno,Sname,Sage,Sdept),其主键为 Sno,表结构定义如图 2-29 所示。

Student 表对应的 Object Layout 如图 2-30 所示。

图 2-29 表结构 图 2-30 Student 表的 Object Layout

到此为止,就建立了一个名为 Pbdb 的数据库。

2.7.2 应用数据库

应用数据库涉及的内容比较多,例如对表本身进行创建、修改及删除等操作;对于一

个结构已经确定了的表,可以对其中的信息进行查询、修改、插入、删除等操作;可以建

立基于基本表的视图,并对视图进行查询、插入、修改、删除等操作。为了简单起见,在

第 2 章 使用 Pow erBuilder操纵数据库

— 41 —

此仅以 Student 表的插入和查询操作为例来说明数据库的应用。

1. 向数据库中插入数据

利用 PowerBuilder 本身提供的快捷工具 Paste SQL 来完成数据库的查询操作。单击工

具栏中的 Paste SQL 工具,选择 insert,然后根据 PowerBuilder 提供的向导一步一步进行

操作。例如,要向 Student 表中插入 5 条记录,其主键学号分别为“张小敏”、“王小明”、

“李英”、“刘芳”和“赵波”。其对应的 SQL 语句如下:

INSERT INTO "student"

( "sno",

"sname",

"sage",

"sdept" )

VALUES ( ’4103034001’,

’张小敏’,

20,

’CS’ );

INSERT INTO "student"

( "sno",

"sname",

"sage",

"sdept" )

VALUES ( ’4103034002’,

’王小明’,

22,

’MS’ );

INSERT INTO "student"

( "sno",

"sname",

"sage",

"sdept" )

VALUES ( ’4103034003’,

’李英’,

20,

’ES’ );

INSERT INTO "student"

( "sno",

"sname",

"sage",

"sdept" )

VALUES ( ’4103034004’,

’刘芳’,

20,

’PS’ );

INSERT INTO "student"

( "sno",

"sname",

"sage",

"sdept" )

Pow erBuilder9.0 基础应用与系统开发

— 42 —

VALUES ( ’4103034005’,

’赵波’,

20,

’CS’ );

然后单击工具栏中的 Execute 按钮,完成 SQL 操作。

2. 查询数据库

上面建立了一个 Student 表并向其中输入了信息,在此依然对此表进行查询操作。其

对应的 SQL 语句如下:

SELECT "student"."sno",

"student"."sname",

"student"."sage",

"student"."sdept"

FROM "student";

单击工具栏中的 Execute 工具,完成 SQL 操作,操作结果如图 2-31 所示。

图 2-31 数据库查询结果

2.8 练习题

(1) 什么是 ODBC 接口,ODBC 接口与专用接口有什么区别?请分别用专用接口和

ODBC 接口建立一个数据库连接,并说明建立的步骤。

(2) 建立一个 PowerBuilder 应用程序,体会工作区、目标程序、应用库和应用对象之

间的层次关系。

(3) 数据库服务器指一台具体的计算机,这种说法正确吗?为什么?

(4) 利 用 数 据 库 画 板 创 建 一 个 数 据 库 , 在 此 数 据 库 中 创 建 一 个 学 生 表

Student(Sno,Sname,Sage,Sdept)和一个成绩表 Scores(Sno,Course,Score),并在 Student 表的

Sno 属性上建立 Student 表的主键,在 Scores 表的 Sno,Course 属性上建立 Scores 表的主

键,在 Scores 表的 Sno 属性上建立 Scores 表的外键。

(5) 通 过 上 题 中 的 Student 表 和 Scores 表 创 建 一 个 视 图

Student_Scores(Sno,Sname,Course,Score),并能对视图进行查询操作。

(6) 什么是存储过程?什么是触发器?它们各自的适用场合分别是什么?

(7) 什么是事务?什么是事务对象?它们分别具有什么特点?

(8) 什么是 SQLCA?它和自定义事务对象在使用过程中有什么不同?

第 3 章 Pow erS cript 语言

PowerScript 是 PowerBuilder 开发环境所使用的编程语言。由语句、命令、函数、集

合、用户自定义函数和嵌入式 SQL 语句组成。PowerScript 是一种高级的、结构化编程语

言。该语言的语法与 Visual Basic 和 C/C++相类似,因此,对于曾经使用过高级语言的编

程人员来说,学习起来比较容易。据评价,PowerScript 是书写代码最少、效率最高的语

言之一。

3.1 基础语法

PowerScript 语言是一种自由格式的语言,如空格、缩进等编辑格式完全被编译器忽

略。所以在编写代码的过程中,书写格式很灵活,用户将感到十分方便。好的书写风格

将起到事半功倍的效果。用户书写的代码应具有层次感,这样既便于阅读,又便于排

错。

3.1.1 大小写与标识符

PowerScript 是大小写不敏感的(Non case sensitive)语言。也就是说,变量 li_total、

LI_TOTAL、Li_Total 和 lI_tOTaL 对于编译器来说是一样的。这条规则同样适用于任何标

识符,包括变量、保留字、内部函数、方法、对象名和控件名等。

3.1.2 注释

PowerScript 的注释可以对程序加以说明,同时也能使某些语句不执行,以便于调

试。PowerScript 的注释方法有两种类型:行注释和块注释。使用方法见表 3-1。

表 3-1 注释方法

注释方法 注释符号 适用范围 说 明 双斜杠(行注释) // 把//右边的内容作为注释 只能用于一行中

斜杠星号(块注释) /* � */ 把注释符中间的内容作为注释 可用于多行

行注释语法:行注释从双斜杠开始到本行结束。可以把行注释符放在行首,使整个行

都成为注释,也可以把它放在程序代码的后面做注释,例如:

//这是一个行注释

dw_1.object.t_title.Text = "申请学士学位统计表" //修改标题

块注释法:块注释以/*开始,到*/结束,在其中所放的任何内容,不管包含多少行,

均作为注释,在编译时被忽略。块注释一般放在函数和事件前面,用来说明代码的作用、

参数和返回值等的重要信息。例如:

Pow erBuilder9.0 基础应用与系统开发

— 44 —

/*********************************************************

该函数使数据窗口更好看

参数:

pi_width: 数据窗口控件的宽度。

pi_height: 数据窗口控件的高度。

返回值:

success: 1 表成功, 0:表失败

*********************************************************/

PowerScript 的块注释可以嵌套,但/*和*/一定要成对出现,否则会出现语法错误。

在 PowerBuilder 的编辑器中简化了行注释的操作。只要将鼠标移到要注释的行,然

后右击,在出现的快捷菜单中单击菜单项 Comment selection 即可做注释。用同样的方

法,单击菜单项 Uncomment selection 可取消注释。

3.1.3 断行与续行

PowerScript 不要求在每一行的末尾加一个分号作为分隔符,只要键入一个 Enter 键,

PowerBuilder 就认为是开始一条新命令。

PowerBuilder 也有一个行分隔符(;),如果在一行中写多条命令,可以用分号来分

隔,例如:

Integer li_mingci; string ls_student_name // 定义名次,学生姓名

PowerScript 有一个续行符,尽管可以使用代码编辑器中的滚动条查看较长的语句,

但是让代码延长到正常窗口的边界之外并不是一种好的做法,使用续行符(&)可以使一条

命令分成两行或多行写。例如:

this.modify("t_total_"+string(ii_i)+ &

string(ii_j)+".text=" + &

"’"+string(ii_total[ii_i, ii_j])+"’")

但是必须保证保留字和变量名的连续,不能将它们断开,若在名字中间换行,则会出

现编译错误。

3.1.4 保留字

PowerBuilder 内部使用的一些命令为保留字(reserved words)。保留字为系统专用,并

有专门的作用,故保留字不能用做标识符,或另作它用,若有违背将产生编译错误。

PowerBuilder 中的保留字如下。

Alias、and、autoinstantiate、call、case、catch、choose、close、commit、connect、

constant 、 continue 、 create 、 cursor 、 declare 、 delete 、 describe 、 descriptor 、 destroy 、

disconnect 、 do 、 dynamic 、 else 、 elseif 、 end 、 enumerated 、 event 、 execute 、 exit 、

external、 false、 fetch、 first、 for、 forward、 from、 function、global、goto、halt、 if、

immediate、indirect、insert、into、intrinsic、is、last、library、loop、next、not、of、on、

open、or、parent、post、prepare、prior、private、privateread、privatewrite、procedure、

protected 、 protectedread 、 protectedwrite 、 prototypes 、 public 、 readonly 、 ref 、 return 、

第 3 章 Pow erScript语言

— 45 —

rollback、rpcfunc、select、selectblob、shared、static、step、subroutine、super、system、

systemread、 systemwrite 、 then、 this、 throw、 throws、 to、 trigger、 true、 try、 type、

until、update、updateblob、using、variables、while、with、within 和_debug。

3.1.5 操作符和优先级

1. Operator(操作符)

Operator( 操 作 符 ) 是 把 一 个 和 多 个 操 作 数 连 接 起 来 作 指 定 运 算 的 符 号 。 在

PowerBuilder 中,有 4 类操作符,即算述运算符、关系运算符、逻辑运算符和连接操作

符。

2. Arithmetic operator(算术运算符)

Arithmetic operator(算术运算符)用于算术运算,基本运算符见表 3-2。

表 3-2 基本运算符与含义

算术运算符 含 义 举 例 + 加法 ii_Total = ii_SubTotal + ii_Tax

- 减法 ii_Price = li_Price - li_Discount 如果将字符�-�作为标识符的一部分时,运算符�-�两边必须留空格

* 乘法 ii_Tota l = ii_Quantity * ii_Price / 除法 Ii_Factor = li_Discount / li_Price ^ 指数运算 li_Rank = li_Rating ^ 2.5

表 3-3 说明了扩展的算术运算符,这些操作符与 C 语言一致,只能做单目运算,不能

将它们和其他运算符合并使用。

表 3-3 扩展的算术运算符

扩展算术运算符 含 义 举 例 等价语句 ++ 自加 1 li_count ++ li_count = li_count + 1 -- 自减 1 li_count -- li_count = li_count–1 += 加 li_count += 3 li_count = li_count + 3 -= 减并赋值 li_count -= 3 li_count = li_count–3 /= 除并赋值 li_count /= 3 li_count = li_count / 3 ^= 幂并赋值 Rank ^= 2.5 Rank = Rank ^ 2.5

有一些运算会引系统错误,例如试图被 0 除,这时应用程序给出如图 3-1 所示的错

误,并退出。

图 3-1 被 0 除错误提示

Pow erBuilder9.0 基础应用与系统开发

— 46 —

3. Relational operator(关系运算符)

Relational operator(关系运算符)在两个操作数之间进行比较,它的结果为 TRUE、

FALSE 或 NULL,关系运算符见表 3-4。

表 3-4 关系运算符及其含义

关系运算符 含 义 = 等于

> 大于 < 小于 <> 不等于 >= 大于等于 <= 小于等于

如果是对字符串进行关系比较,要注意比较对大小写是敏感的。为了确保字符串匹

配,用户需要使用下列函数。

RightTrim(string):该函数去掉字符串尾部空格。

LeftTrim(string):该函数去掉字符串开头的空格。

Trim(string):该函数的作用是去掉字符串前面和结尾的空格。

Upper(string):该函数将字符串的每个字符转化成大写以组成新的字符串。

Lower(string):该函数将字符串的每个字符转化成小写以组成新的字符串。

4. Logical operator(逻辑运算符)

Logical operator(逻辑运算符)用于形成布尔表达式,这些表达式的结果为布尔型,其

值要么为真 TRUE,要么为假 FALSE。逻辑操作符见表 3-5。

表 3-5 逻辑运算符及其含义

逻辑操作符 含 义 NOT 逻辑非 AND 逻辑与 OR 逻辑或

除非用户创建的表达式十分简单,否则将相关的部分放在括号内是一个好的编程习

惯,这样有助于用户理解表达式,利于调试和维护,而不需要去判断操作符的优先级,避

免出错。

5. Logical operator(连接操作符)

Logical operator(连接操作符)用于连接两个变量的内容。这两个变量必须都是 String

或都是 Blob 数据类型。

6. 操作符的优先级

用操作数和运算符连起来的式子称为表达式,在求表达式值的过程当中,操作符是按

特定的优先级顺序进行运算的,优先级高的先运算。用户也可以通过用括号对表达式分

组,以改变操作符的执行顺序而达到预期结果。嵌套的括号从内向外运算,当操作符优先

级相同时,从左向右运算。

第 3 章 Pow erScript语言

— 47 —

表 3-6 按优先级从高到低的顺序列出操作符。

表 3-6 操作符的优先级

操作符 含 义

+,- 一元加和一元减(相当于正负号) ^ 指数运算 *,/ 乘和除 +,- 加、减和连接 =, >, <, <=, <=, <> 关系运算符 NOT 逻辑非 AND 逻辑与 OR 逻辑非

3.2 数据类型

PowerBuilder 与 C 语言一样,它是一种数据类型敏感的开发语言,它提供了标准的和

增强的数据类型。

3.2.1 标准数据类型

在 PowerBuilder 中规定了可以使用的标准数据类型,它们在许多不同的编程语言和

数据库服务器之间都是通用的。然而,用户应该清楚 PowerBuilder 的数据类型,以避免

在数据转换的过程中降低数据的精度。例如:

int li_1

double ld_1

ld_1 = 3.1415 // ld_1 的值为 3.1415

li_1 = ld_1 // li_1 的值为 3

ld_1 = li_1 // ld_1 的值为 3

在这里,显然由于将双精度数传递到整型变量中,从而导致数据精度的降低。

表 3-7 列出了所有标准的数据类型及其定义,并给出了每种类型的示例。

表 3-7 PowerBuilder 中的标准数据类型

数据类型 定 义 例 子 Blob 二进制大对象,用于存储大量数据 位图 Boolean 布尔型(TRUE、FALSE) FALSE

Character 或 Char 单个 ASCII 字符 A Date 日期(年月日) 2001-7-28 Datetime 将日期和时间合并成一个数据类型 2001-7-2811:58 Decimal 或 Dec 有符号十进制数,精度可达到 18 位 3567.7777778

Double 有符号浮点数,15 位精度,作用域从 2.2E-308 到1.7E+308

5.3E+29

Integer 或 Int 16 位有符号数,作用域从-32768 到+32767 7322 Long 32 位有符号数,范围从-2147483648 到+2147483647 127506 Real 有符号数,6 位精度,范围从 1.17E-38 到 3.4E+38 3.9E-5

String 任意 ASCII 字符,长度可以从 0 到 59999,或象系统

提供的一样大 "这是一个字符串

"

Time 24 小时格式的时间值:时、分、秒及毫秒数(达到 6位)

16:36:35:250

UnsignedInteger 或 UnsignedInt 或 Uint 16 位无符号整数,范围从 0 到 65535 6206

UnsignedLong 或 Ulong 32 位无符号数,范围从 0 到 4294967295 6206016

Pow erBuilder9.0 基础应用与系统开发

— 48 —

3.2.2 增强数据类型

除了标准数据类型外,在 PowerBuilder 中还提供了增强的数据类型——Any 数据类

型。

该变量类型可以存储各种类型的值,包括 PowerBuilder 中的标准数据类型、对象、

结构和数组,也就是说,Any 类型的变量随着赋值变量的类型可以发生变化。

用户甚至可以定义一个 Any 类型的数组,该数组的每一个元素可以包含不同的数据

类型,可以通过 ClassName 函数来检验类型为 Any 的变量中存储的真正变量类型,下面

的代码可以完成该功能:

any la_spreadsheetdata

la_spreadsheetdata = ole_1.Object.cells(1,1).value

CHOOSE CASE ClassName(la_spreadsheetdata)

CASE "integer"

...

CASE "string"

...

END CHOOSE

使用 Any 数据类型时必须注意以下几点。

(1) 使用任何类型的值赋予 Any 变量。

(2) 当用 Any 型变量给确定类型赋值时,必须保证被赋值的变量和该 Any 类型变量

的类型相容,例如:

Any la_count

Integer li_count = 10

String ls_name = "Zhang san"

La_count = li_count

Ls_name = la_count //该语句在编译时不会出现错误

但在执行时将会出现如图 3-2 所示的错误提示。

图 3-2 Any 变量赋值错误

(3) 当用 Any 型变量给确定性变量赋值时,不能使用“.”操作符访问结构中的具体

元素,必须将该 Any 类型变量赋予一个确定的结构变量。

(4) 当一个 Any 型变量被其他变量类型赋值后,就不能将该变量转换为 Any 类型,

即使该变量被赋予空值(NULL)。

当给 Any 型变量赋值之后,可以使用与该赋值变量类型合法的任何操作符,如果使

第 3 章 Pow erScript语言

— 49 —

用了该变量类型不支持的操作符,不会出现编译错误,但会出现执行错误,例如:

int li_1, li_2

li_1 = 2

li_2 = 4

any la_3

la_3 = li_1 + li_2

以上代码是正确的,以下代码执行时是错误的。

int li_1

string ls_2

li_1 = 2

ls_2 = �4� any la_3 = li_1*ls_2

可以对 Any 类型变量进行数据转换,但要注意,Any 类型变量中保存的值的具体类

型必须能够转换为特定的数据类型。例如可以使用下列代码,将 Any 型变量中存放的整

数转换为字符串。

int li_1 = 2001 any la_any string ls_string la_any = li_1 ls_string = string(la_any)

在开发应用程序时要尽量避免使用 Any 型变量,因为 Any 型变量使程序的执行速度

变慢,尤其是在循环体内使用 Any 型变量时会使性能大大下降;同时,使用 Any 型变量

编程时容易使程序隐藏编译错误,只有在程序执行时,才会提示错误的发生。

3.2.3 对象型数据类型

在 PowerBuilder 中,系统对象是一类特定的数据类型,可以通过在 PowerBuilder 的

Window 菜单的 Browse 菜单项所弹出的对话框的 System 标签页中列出,如图 3-3 所示。

图 3-3 系统对象

用户在设计 PowerBuilder 应用的时候,可以操纵诸如:windows、menus、command

Pow erBuilder9.0 基础应用与系统开发

— 50 —

buttons、list boxes 和 graphs 等系统对象。在 PowerBuilder 内部,每一种对象被定义为一

种数据类型。对于用户来说,可以不去关心这些数据类型,只要在各种对象画板中设计可

视对象并使用它们即可。

但是用户还是应该理解 PowerBuilder 是如何在数据类型框架中维护系统对象的。例

如,当用户定义了一个窗口的实例变量后,实际得到一个 Window 数据类型的变量。

在 PowerBuilder 中,系统对象形成自己的类结构(Class hierarchy)。类的具体概念属于

面向对象的术语,在此不再详述。每一个系统对象均是一个类。类与类之间可能有父子关

系(Ancesters and descendants)。在图 3-4 中显示的所有类都可以被当做一种数据类型。在

程序中可以定义任何类型的变量,例如:

window w_main // 定义一个名为 w_main 的 window 数据类型

menu m_mainmenu // 定义一个名为 m_mainmenu 的 menu 数据类型

图 3-4 系统的枚举类型

如果在某一个用户窗口中定义了一系列按钮,由于某种原因,想知道用户单击了哪一

个按钮,可以声明一个 CommandButton 类型的变量,用被单击的按钮给它赋值。程序语

句为:

// Instance variable in a window

commandbutton LastClicked

// In Clicked event for a button in the window.

// Indicates that the button was the last one clicked by the user.

同时,在每个按钮的事件里加入代码:

LastClicked = This

3.2.4 枚举型数据类型

像系统对象类型一样,枚举数据类型(Enumerated)是 PowerScript 的一种特定的数据类

型。枚举数据类型一般用于下列两种情况。

(1) 作为函数的参数。

(2) 作为对象和控件的属性。

第 3 章 Pow erScript语言

— 51 —

可以在浏览器窗口(Browse)中查看到所有的枚举数据类型以及它们的取值,如图 3-4

所示。

一个枚举类型的变量可以被赋值为该枚举类型的所有取值的任意一个,枚举类型的每

一个值必须以感叹号(!) 结束。

例如:文本的对齐方式是一种枚举类型,它共有 3 个可能的取值:Center!、Left!和

Right!,代码如下:

mle_edit.Alignment=Right! //被置为右对齐

3.2.5 数据类型的转换

PowerBuilder 提供了一些数据转换函数,利用它们可以方便地将一种数据类型转换为

另一种数据类型,10 种转换函数见表 3-8。

表 3-8 PowerBuilder 的转换函数

转换函数 含 义 Char 将 Blob、Integer 或 String 转换为 Char 型 Dec 将 String 转换为 Decimal 型 Double 将 String 转换为 Double 型 Integer 将 String 转换为 Integer 型 Long 将 String 转换为 Long 型 Real 将 String 转换为 Real 型 Date 获取一个 Datetime 值的 Date 部分 Datetime 将 Date 转换为 Datetime 型 String 将 Blob、Date、Datetime 和 Time 转换为 String 型 Time 获取 Datetime 值的 Time 部分

同时 PowerBuilder 还提供了一些函数用来检测变量的数据类型。

(1) IsDate(datevalue):检测 Datevalue 变量是否包含一个合法的日期字符串,如果

是,返回 TRUE,否则返回 FALSE。

(2) IsNumber(string):检测 String 变量是否包含一个合法的数字字符串,如果是,返

回 TRUE,否则返回 FALSE。

(3) IsTime(timevalue):检测 Timevalue 变量是否包含一个合法的时间字符串,如果

是,返回 TRUE,否则返回 FALSE。

(4) IsNull(any):检测 Any 变量是否为空,如果是,返回 TRUE,否则返回 FALSE。

3.2.6 字符与字符串

字符是单个 ASCII 元素,而字符串是 0 个或多个字符的集合。如果用户将一个字符

串赋值给一个字符变量,则只保存第 1 个 ASCII 值。

字符数组也可以赋值给字符串变量,拷贝时遵从下列规则。

(1) 可以直接将一个字符串拷贝到一个无界的字符数组。

(2) 拷贝到一个有界数组的字符串将截断到数组上界。

(3) 如果字符串比字符数组上界短,未使用的元素初始化为空值。

下面给出一段代码,说明上述规则:

Pow erBuilder9.0 基础应用与系统开发

— 52 —

char lc_1[],lc_2[5],lc_3[1]

string ls_1

ls_1 = �sd� lc_1 = ls_1 lc_2 = ls_1 lc_3 = ls_1

在调试窗口中可以看到拷贝执行后各变量的结果,如图 3-5 所示。

图 3-5 调试窗口

在数据库程序设计中,最常用的数据类型就是字符串,PowerBuilder 提供了大量的函

数进行字符串处理。

(1) Asc(string):将字符串的第 1 个字符转换为 ASCII 码值,函数返回值为整数。

(2) Char(n):将 ASCII 值转换为字母,函数返回值为字符型或字符串型。

(3) Fill(char, n):重复一个字符 n 次,将该字符串赋值给字符串变量,函数返回值为

字符串型。

(4) Left(string, n):取字符串 string 的前 n 个字符组成的字符串作为函数返回值。

(5) LeftTrim(string):去掉字符串的开头空格,函数返回值为字符串。

(6) RightTrim(string):去掉字符串的结尾的空格,函数返回值为字符串。

(7) Len(string or blob):得到 string 或 blob 数据的长度,函数的返回值为长整型。

(8) Lower(string):将字符串的每一个字符变为小写,函数返回值为字符串。

(9) Match(string, texpattern):测试字符串中是否含有特定的 textpattern 子串,函数返

回值为布尔型。

(10) Mid(string, start{,length}):从字符串 string 中从 start 开始,取长度为 length 的子

串,函数返回值为字符串型。

(11) Pos(string1, string2{,length}):从长度 length(默认值为1)开始,在字符串 string1

中查找子串 string2,返回子串 string2 在 string1 中的起始位置,类型为长整型。若没找

到,则返回值为0。

(12) Replace(string1, start, n, string2):返回用 string2 替换 string1 从 start 开始的长度为

n 的子串而得到的字符串。

(13) Reverse(string):返回字符串 string 的倒置,返回值为字符串型。

第 3 章 Pow erScript语言

— 53 —

(14) Right(string, n):返回字符串 string 的右面的 n 个字符所组成的子串,返回值为字

符串型。

(15) Space(n):返回由 n 个空格字符组成的长度为 n 字符串。

(16) Trim(string):返回去掉字符串 string 开头和结尾的空格字符后所组成的字符串。

(17) Upper(string):返回字符串 string 的每个字符转换为大写后的字符串。

PowerBuilder 允许用户在字符串中使用特殊的 ASCII 字符,使用方法类同于 C 语

言,只是在 PowerBuilder 中引入了特殊的代字符(~),该代字符类同于 C 语言的(\)。表 3-9

列出了在 PowerBuilder 中如何指定 ASCII 字符。

表 3-9 用于 PowerBuilder 中的 ASCII 字符

字符串 生成的 ASCII 字符 ~n 换行 ~t 制表符 ~v 垂直制表符 ~r 回车 ~f 换页符 ~b 退格符 ~� 双引号 ~� 单引号 ~~ 代字符 ~000-~255 用十进制表示的 ASCII 码 ~h01-~hff 用十六进制表示的 ASCII 码 ~o000-~o377 用八进制表示的 ASCII 码

3.2.7 数组

数组是有共同的名字,通过下标来访问的具有相同数据类型的一组变量。可通过惟一

的下标来访问数组的每一个元素,数组的元素可以是任何数据类型,数组的维数可以是一

维或是多维。

用下列语法定义数组:

{access}datatype variablename [{d1, �, dn}] = {valuelist}

1. 一维数组

一维数组是一个一维表,用单个数字来说明表中元素的个数,或用 TO 语句来设置下

标范围。声明确定长度的一维数组的方法如下:

string lsa_a1[12] string lsa_a2[10 to 20]

第 1 个语句声明了一个有 12 个元素的字符串数组,其索引从 1 到 12。第 2 条语句声

明了一个 11 个元素的字符串数组,其下标从 10 到 20。这两个数组均是固定长度的字符

数组。若是引用作用域以外的下标,将会产生错误。

注意

编译器在编译的时候并不检查数组的越界,只有在程序运行的时候才会报告错误。所

以在使用数组的时候,一定要检查数组的下界和上界,以避免数组越界。

Pow erBuilder9.0 基础应用与系统开发

— 54 —

2. 无界数组

无界数组(Unbounded Array)或大小可变数组(Variable Size Array)是没有定义下标作用

域的一维数组,多维数组不允许定义无界数组。

无界数组的下标从 1 开始,不能更改,所以不能使用 TO 关键字。上界由最大的下标

值控制。一个可变大小的整数数组可以声明如下:

integer li_array[]

按照这种声明方式,任何大于 1 的索引引用均是合法的,然而用户应知道无界数组的

内存分配方式。例如,当下列三种情况第 1 次执行的时候,内存分配如下。

(1) li_array[100] = 2000:为 li_array 数组分配 100 个整数值内存。从 li_array[1]到

li_array[99]的值为 0,li_array[100]的值为 2000。

(2) li_array[50] = 3000:在执行该语句的时候,系统不再为数组分配内存,只是

li_array[50]被赋值为 3000。

(3) li_array[110] = 5000:在执行该语句的时候,系统再给数组 li_array 分配 10 个整型

数值的内存,从 li_array[101]到 li_array[109]被赋值为 0,li_array[110]被赋值为 5000。

注意

当使用无界数组的时候,每一次内存分配都耗费一定的系统时间,所以为了提高系统

的性能,尽量不要使用无界数组。

访问一个无界数组当前作用域之外的下标值,将产生一个运行错误,例如下列代码不

合法。

String ls_1[]

Ls_1 = �god� MessageBox(“错误”,ls_1[6]) // 该语句引用 ls_1[6],超过了数组的上界

3. 数组的上下界

有两个 PowerBuilder 的函数用来确定数组的上界和下界。

(1) LowerBound(array{,n}):返回数组 array 第 n 维的下界,返回值为长整形,n 默认

值为 1。当参数 n 超过数组的最大维数时,该函数返回-1,当 array 的值为 NULL 时,

LowerBound 函数返回 NULL。

当无界数组在分配内存值之前,该数组的下界为 1,上界为 0。举例如下:

integer a[5], b[2,5] LowerBound(a) //返回为 1 LowerBound(a, 1) //返回为 1 LowerBound(a, 2) //返回为-1,因为该数组没有第 2 维 LowerBound(b, 2) //返回为 1 Integer c[] LowerBound(c) //返回为 1 c[50] = 75 LowerBound(c) //返回为 1

第 3 章 Pow erScript语言

— 55 —

Integer d[-10 to 50]

LowerBound(c) //返回为-10

(2) UpperBound(array{,n}):返回数组 array 第 n 维的上界,返回值为长整形,n 默认

值为 1。该函数的其他特性类同于函数 LowerBound,举例说明如下:

integer a[5], b[2,10]

UpperBound(a) //返回为 5

UpperBound(a, 1) //返回为 5

UpperBound(a, 2) //返回为-1,因为该数组没有第 2 维

UpperBound(b, 2) //返回为 10

Integer c[]

UpperBound(c) //返回为 0

注意

UpperBound 及 LowerBound 函数的执行非常耗费时间,所以应尽量避免在循环体中

使用。

4. 数组的初始化

可以在声明数组时给数组赋初值,这一点和其他的数据类型类似。初值的数据类型必

须与数组元素的数据类型相同。固定大小的数组的初值个数必须与数组的元素个数相同。

语法格式是用大括号将数值括起来,并以逗号分隔各个数值形成列表,例如:

string ls_1[4] = {"s","s","s","s"}

也可以将一个数组直接赋值给另一个数组,例如:

string ls_1[]

string ls_2[4] = {"s","s","s","s"}

ls_1 = ls_2

5. 多维数组

多维数组只能是固定大小,它包含一维以上的声明,数组中元素的引用顺序是,从各

维下标最小值开始,较大维数下标先作变化。例如记录一幅图片,可定义一个二维数组:

integer pixel[640,480]

该数组由 640*480 即 30720 个元素组成,该数组的引用顺序为:

pixel[1, 1], pixel[1, 2], pixel[1, 3], ⋯⋯,pixel[1, 479], pixel[1,

480],

pixel[2, 1], pixel[2, 2], pixel[2, 3], ⋯⋯,pixel[2, 479], pixel[2,

480],

⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯

pixel[640, 1] , pixel[640, 2] , pixel[640, 3] , ⋯ ⋯ , pixel[640, 479] ,

pixel[640, 480],

下面列出的声明多维数组的方法都是正确的:

Pow erBuilder9.0 基础应用与系统开发

— 56 —

integer li_array[20,3]

char li_chararray[4, 0 to 255]

long ll_days[3, 4 to 200, -20 to 20]

3.3 变量与常量

用户可以定义用于自己脚本内的变量,定义这些变量要确定它们的存取类型、作用

域、数据类型和初始值。变量可以是简单的数据类型、对象类型和结构类型等。

3.3.1 实例变量的访问控制

变量的访问控制只能对实例变量进行,对于一般的变量,不允许使用,否则产生编译

错误。

当用户对实例变量施加了访问控制后,就可以控制哪一段代码能访问该变量。

其语法为:

{accessright}{readaccess}{writeaccess}datatype variablename

各参数的含义如下。

accessright(可选),该项限定变量的访问权限。可选的值如下。

(1) PUBLIC(默认):全局访问变量,在程序的任何代码中均可访问该变量。在本对象

内的代码可以直接访问,在该对象之外的代码,必须使用点操作符进行访问。

(2) PROTECTED:保护访问变量,在声明的该变量的对象和其子孙对象里可以访问

该变量。

(3) PRIVATE:局部访问变量,只有声明该变量的对象才能访问该变量,该对象的子

孙对象也不能访问该变量。

readaccess(可选),该关键字限制脚本对变量值的读取,可选的值如下。

(1) PROTECTEDREAD:在声明该变量的对象和其子孙对象里可以读取,但不能修改

该变量。

(2) PRIVATEREAD:只有声明该变量的对象才能读取该变量,该对象的子孙对象也

不能访问该变量。该变量不能被修改。

当声明了 accessright 为 PUBLIC 时,可以声明 readaccess 为两种取值之中的任何一个

值。当声明了 accessright 为 PROTECTED 时,可以声明 readaccess 为 PRIVATEREAD。当

声明了 accessright 为 PRIVATE 时,不需要再声明 readaccess,因为 PRIVATE 对变量进行

了最大限度的保护。

writeaccess(可选),该关键字限制脚本对变量值的更改,可选的值如下。

(1) PROTECTEDWRITE:在声明该变量的对象和其子孙对象里可以修改该变量。

(2) PRIVATEWRITE:只有在声明该变量的对象中才能修改该变量。

datatype:指定变量所用的数据类型,可用的数据类型请参照 3.2 节的介绍。

variablename:正确的变量标识符。

声明访问控制的作用在于,它可以控制最终用户访问特定对象变量的权限。一个典型

第 3 章 Pow erScript语言

— 57 —

的用法是,在对象中声明一个 Public 类型的变量,但是只允许它的所有者可以对该变量

进行修改,而其他的对象只能读取该变量,不能对它进行修改。其语句为:

Public protectedwrite integer ii_count

声明为 protected 的变量只有它的所有者才能对它进行修改,所有者对象的子孙对象

可以对该变量进行读取,但不能修改,其声明语句如下:

protected privatewrite string is_name

下面的例子定义变量具有 PUBLIC(默认)存取特性,但是只能在该对象的代码中对该

变量进行修改,在对象代码之外无法修改。

假设用户定义了一个窗口对象 w_subwin,在该窗口对象中定义了一个实型变量

ir_v1:

Public privatewrite real ir_v1

下面的代码将会产生编译错误:

w_subwin w_mysubwin

w_mysubwin.ir_v1 = 1

因为该变量的修改控制特性被定义为 privatewrite,所以在对象外修改该变量将导致

错误。

3.3.2 变量的作用域

可以定义某个变量的作用域来表示如何访问该变量,何时访问该变量,以及如何在不

同的作用域中声明同名变量,哪一个变量被首先引用。作用域有:全局、共享、实例和局

部变量。

1. 全局变量

全局变量(Global variables)定义为整个应用程序都可以访问、并存储在应用程序对象

中的变量。与其他编程语言一样,应该使全局变量的数目保持为最小。因为任何脚本的任

何地方都可以修改全局变量,从而导致程序的耦合性太强,内聚性差,程序的可靠性难以

把握,当然有少量的全局变量也是必不可少的。全局变量在应用程序执行期间使用同一块

内存,在开始程序执行时,对全局变量进行初始化。

2. 实例变量

实例变量(Instance variables)与类对象实例相关联,在一个对象(如:应用程序、窗

口、用户对象或菜单)中定义,因而可以在该对象的任何的地方引用它们。根据访问权限

的不同,它们其中的一些也可以用点操作符在对象的外部访问。

实例变量实际是定义它的对象的属性,这些变量在该对象创建时产生并初始化,当该

对象被删除时,实例变量也随之消失,一个实例变量不能在两个实例中共享。

3. 共享变量

共享变量(Shared variables)在与实例变量相同的对象中定义。然而,共享变量与实例

Pow erBuilder9.0 基础应用与系统开发

— 58 —

的类相联系,而不是与对象实例相联系。这意味着同一个类的所有实例共享同一个变量。

共享变量总是一个 Private 类型的变量,只能在定义它们的类中对它们进行访问。

在第 1 次创建该类的实例时,创建并初始化该共享变量,随后当删除该类的实例后,

该共享变量并不消失,当再次创建该类的实例时,共享变量依然保持着原来的值,若用户

同时创建了该类的多个实例,则它们均共享该实例变量。

例如:窗口 w_stu 由一个共享变量声明为 long xh = 1000001,当 w_stu 第 1 次被初始

化时,创建变量 xh 并赋初值为 1000001,之后,xh 被修改为 1000002,关闭该窗口,当

再打开该窗口时,原来的 xh 并没有消失,而且这次打开并不必对该变量赋初值,其值仍

然是 1000002。

请读者特别注意实例变量和共享变量的区别。

4. 局部变量

局部变量(Local variables)就是在脚本层定义的变量。它只能用于脚本内并在脚本末尾

被删除。局部变量用于保存数据计算的中间结果。

5. 优先级顺序

在一个脚本执行期间,PowerBuilder 按下列顺序搜索变量。

(1) 局部变量。

(2) 共享变量。

(3) 全局变量。

(4) 实例变量。

如果 PowerBuilder 无法在上面的任一层找到该变量,它就向上搜索该对象的继承

链,查找继承链的实例变量。

3.3.3 常量

任何可以在声明中给出初始值的变量均可以通过添加关键字 constant 转变为一个常量

的声明。在声明常量后,用户就不能对它的值进行修改。例如:

constant string COMPANY_NAME = �PowerSoft�

用户可以将常量的名字全部使用大写,这样在程序中可以起到一目了然的作用,降低

程序的维护负担,提高程序的可读性。

使用常量的好处是,用户可以从常量的字面含义中得到其值,而不必记住其具体值,

若在一段代码中多次使用同一个数值,那么用常量可以保持数值的一致性,避免修改的遗

漏,使维护工作简单。

注意不能在同一段代码中对常量进行二次声明,否则导致编译错误。此外,在一行语

句中只能声明一个常量。

3.4 代词

在 PowerBuilder 中有 3 个代词 this、parent 和 super,它们都有特殊的含义。它们所代

第 3 章 Pow erScript语言

— 59 —

表的意义取决于在什么地方使用它们。当创建用于继承的对象或封装对象时,使用代词可

以提供用户代码的重复使用。

在 PowerScript 中使用代词可以引用对象和控件。当使用代词时,即使原控件的名字

改变,也不会造成代码错误,使程序容易维护。

用户可以在函数或事件中使用代词来代表对象和控件,一般用于下列情况之一。

(1) 在对象或控件的代码中触发事件。

(2) 引用或修改对象或控件。

(3) 获得或修改对象或控件的属性。

每一个代词的含义见表 3-10。

表 3-10 PowerScript 中的代词

代 词 应用的对象 指代的对象 This 窗口、用户对象、菜单、应用程序对象或控件 对象或控件本身

Parent 窗口中的控件 用户对象中的控件 菜单

包含该控件的窗口 包含该控件的用户对象 包含该菜单的上层菜单

Super 子孙对象或控件 子孙窗口或用户对象 在子孙窗口或用户对象中的控件

父类 子孙窗口或用户对象的直接祖先 包含该控件的子孙窗口或用户对象的直接祖先

与代词类似的还有一个名为 ParentWindow 的属性(property),该属性属于菜单,它代

表了拥有该菜单的窗口。

3.4.1 This

This 代词可以用于窗口、用户对象、菜单、应用程序对象或控件的代码中,用于指

代对象或控件本身。

使用 This 代词可以隐式地引用对象本身,下面的代码引用对象的 x 属性:

This.x = This.x + 50

在一些场合使用对象本身的名字和使用 This 代词可以起到同样的效果,但是使用

This 代词可以使用户的程序更清晰,维护更方便。

但有时必须使用 This 代词,那就是当全局变量或局部变量与当前的实例变量同名

时,必须用 This 代词指代该实例变量,因为 PowerBuilder 在执行时首先搜索局部和全局

变量。

3.4.2 parent

Parent 用于指示包含该对象的对象,几乎所有对象都有它的父对象。要引用对象的所

有者,应使用保留字 Parent。

可以在以下三种情况下使用 Parent 代词。

1. 在窗口中的对象

当用户在窗口的控件(如数据窗口控件) 中使用 Parent 时,Parent 指代包含该控件的

窗口。

Pow erBuilder9.0 基础应用与系统开发

— 60 —

该代词的最常用的一个用法是在一个 Close 按钮中,这是使用 Parent 时代码成为通用

的。下面两个代码实例在功能上是相同的,但第 1 个是通用的,使该对象成为非常适于重

复使用的对象:

Close(Parent) //关闭该窗口

Close(w_script_window) //功能同上,但只适用于特定的窗口

用户也可以在窗口中利用 Parent 代码来访问该窗口的另一个控件。例如:

Parent.sle_name.text = “教学计划”

2. 用户自定义对象

当在自定义对象的控件(例如下拉列表)中使用 Parent 时,Parent 指代包含控件的自定

义对象。

例如:用户可以在自定义对象的 NewCommandButton 中的 Clicked 事件加入如下代

码,当用户单击该按钮时,该按钮将隐藏。

Parent.Hide()

3. 菜单对象

当用户在菜单中使用 Parent 时,Parent 指代包含该菜单的上层菜单。

例如:在下级菜单中使用如下代码可以将该菜单的上级菜单变为不可用。

Parent.Disable()

3.4.3 super

只有在处理继承时才使用代词 Super,Super 用于引用一个后代对象的祖先。祖先对

象的名字可以显示说明,但更常用的做法是用 Super 代词来调用祖先对象的函数。如果后

代对象和祖先对象的函数名和参数都相同,那么可以用 Super 代词来调用祖先对象的函

数。

例如下列代码用于调用由后代重载的祖先函数:

Super::wf_function()

再 如 , 下 面 的 代 码 可 以 调 用 CommandButton 对 象 所 在 的 窗 口 的 直 接 祖 先 的

CommandButton 对象中的 Clicked()事件:

Super::EVENT Clicked()

3.5 基本语句

和其他结构化查询语言一样,PowerScript 语言也提供 3 种程序设计的流程:顺序、

分支和循环。

第 3 章 Pow erScript语言

— 61 —

它提供了如下语句。

Assignment( 赋 值 语 句 ) 、 CALL 、 CHOOSE CASE 、 CONTINUE 、 CREATE 、

DESTROY、DO LOOP、EXIT、FOR NEXT、GOTO、HALT、IF THEN 和 RETURN。

这些语句大部分比较简单,这里仅列出语法格式,对其中比较重要和复杂的语句给出

实例。

(1) Choose⋯Case

该语句的语法为:

CHOOSE CASE testexpression //表达式

CASE expressionlist_1 //表达式的可能值 1

statementblock //当表达式为可能值 1 时,执行的语句块

{ CASE expressionlist_2

statementblock

. . .

CASE expressionlist_n

statementblock }

CASE ELSE //当表达式不等于上面的任何一个时,执行下面的语句块

statementblock }

END CHOOSE

例如下面的代码用一个布尔值测试是否按下了某个键,并完成相应的动作:

Choose Case TRUE

Case KeyDown(KeyPageUp!)

Wf_SynchPageUp()

Case KeyDown(KeyPageDown!)

Wf_SynchPageDown()

Case KeyDown(KeyUpArrow!)

Wf_SynchUpArrow()

Case KeyDown(KeyDownArrow!)

Wf_SynchDownArrow()

End Choose

(2) FOR 循环

语法为:

FOR varname = start TO end {STEP increment}

Statementblock //循环体

NEXT

其中 varname 为循环变量,start 为循环初值,end 为循环终值,increment 为循环步

长,默认值为 1。举例如下:

A = 0

FOR n=5 to 25

A = A + n

Next

该例累加计算 0+5+6+⋯+25。

Pow erBuilder9.0 基础应用与系统开发

— 62 —

(3) HALT {CLOSE}

当 PowerBuilder 执行到该语句,而该语句没有 CLOSE 关键字时,应用程序立即关

闭。否则,立即触发应用程序的 CLOSE 事件,然后关闭该应用程序。

(4) CALL 语句调用其祖先对象的方法。如:

CALL w_emp::Open。

(5) CONTINUE 语句用于 DO...LOOP 语句或 FOR...NEXT 语句中,不带参数,作用为

跳过该层循环体的后续语句,当循环条件成立时,做下一次循环。

(6) CREATE 语句给一个特定的对象类型创建一个对象实例,如:

Transaction DBTrans //声明 Transaction 对象类型的对象实例 DBTrans

DBTrans = CREATE transaction //创建该对象实例

DBTrans.DBMS = ’ODBC’ //对其实例的后续操作

(7) DESTROY 语句清除由 CREATE 语句创建的对象实例,如:

DESTROY DBTrans //清除实例 DBTrans

(8) DO... LOOP 语句为循环控制语句,有以下 4 种格式。

格式一:

DO UNTIL 条件

循环体(可有多条语句)

LOOP

格式二:

DO WHILE 条件

循环体(可有多条语句)

LOOP

格式三:

DO

循环体(可有多条语句)

LOOP UNTIL 条件

格式四:

DO

循环体(可有多条语句)

LOOP WHILE 条件

(9) EXIT 语句用于 DO...LOOP 语句或 FOR...NEXT 语句的循环体中,作用为跳出该

层循环,执行该循环的后续语句。

(10) IF THEN 语句为分支语句,语法如下:

IF 条件 THEN 语句组 1 {ELSE 语句组 2 }

当条件成立时执行语句组 1,否则执行语句组 2。

第 3 章 Pow erScript语言

— 63 —

3.6 函数和结构

3.6.1 函数

用户在使用 PowerBuilder 时,可以调用系统定义的函数,也可以使用自己编写的函

数,可以说应用程序的编写离不开函数的使用。PowerBuilder 预定义了一系列函数,共有

800 余个,每个函数都可完成特定的功能。这些函数一般在 PowerScript 语言中调用,可

以完成几乎所有的应用处理工作,功能非常强大。它们按用途可分为 22 类,具体请查看

联机帮助。

1. 函数搜索链

当用户调用一个没有对象名限制的函数时,PowerBuilder 搜索函数的顺序如下。

(1) 全局外部函数。

(2) 全局函数对象。

(3) 局部外部函数。

(4) 对象层函数。

(5) 系统函数。

从上面的搜索顺序可以看出,用户可以用自己定义的函数来覆盖系统内部定义的函数。

2. 参数的传递方式

系统提供了 3 种参数传递方式,为用户自定义或系统函数和事件传递参数,这 3 种方

式如下。

(1) By Value(值传递):传递给函数的参数是原变量的一个拷贝,在函数体内对函数的

修改只影响参数本身,不影响原变量的值。如声明函数 byvalue(integer age)return(null)的

参数 age 采用默认的值传递方法。

(2) By Reference(址传递):这种传递方式是将原变量的指针传递给函数或事件,在函

数或事件中对该参数的修改事实上是对原变量的修改,从而影响原变量。如声明函数

byreference(ref integer age)return(null)的参数 age 采用址传递方法。

(3) Read Only(只读传递):在函数或事件中可以访问到原变量,原变量当做常量,对

原变量的任何修改都是不允许的。只读传递对于某些数据类型的变量来说,可以提高系统

的性能,因为在参数传递过程中,系统并不创建原变量的拷贝。这些变量类型为:

string 、 blob 、 datetime 、 date 和 time 。 如 声 明 函 数 byreadonly(readonly integer age)

return(null)的参数 age 采用只读传递方法。

在分布式程序设计中,参数传递也有 3 种方式,和上面类似,但具体用法有一定的区

别,感兴趣的读者可以查看联机帮助。

3. 特殊类型的参数传递

(1) 传递对象:当用户将对象作为参数传递给函数或事件的时候,必须保证该对象在

函数调用期间存在,否则不论用 3 种参数传递的哪一种方式进行传递,在程序执行的过程

Pow erBuilder9.0 基础应用与系统开发

— 64 —

中将发生空对象引用错误。例如用户为窗口设计了一个函数,该函数的一个参数为窗口的

控件变量,那么该函数不能在窗口关闭后调用,因为此时窗口中的对象已不存在。

当为函数传递对象的时候,系统并不产生对象的拷贝,只是产生一个该对象的引用,

所以不论是以传值方式还是传址方式,对变量的修改均会反应到原变量中。

(2) 传递结构:为函数传递结构和传递普通变量类似,和传递对象不同。

以传值方式传递结构变量,函数传递该结构的一个拷贝,用户在函数体中对结构的修

改,不影响原结构变量的值。

以传址方式传递结构变量,函数传递该结构的一个引用,用户在函数体中对结构的修

改,影响原结构变量的值。

以只读方式传递结构变量,函数传递该结构的一个拷贝,由于该结构是只读的,用户

在函数体中不能对该结构进行修改。

4. 函数调用方式

函数调用方式分静态调用和动态调用。

(1) 静态调用

系统默认使用静态调用。当 PowerBuilder 编译代码的时候,编译器查找该事件或函

数,函数或事件的调用格式、包括参数和返回值,这些都必须和定义相一致,而且在编译

阶段必须已经存在,否则出现编译错误。

(2) 动态调用

用户可以声明一个动态调用。当对函数或事件声明动态调用时,在编译器编译代码的

时候,该函数或事件可以不存在,也就是说,可以暗示编译器:请忽略编译错误,在程序

执行的时候,用户会提供一个合适的函数或事件,以供程序调用。

为更好地说明动态调用的结果,举例如下。

设 祖 先 窗 口 对 象 w_a 中 有 一 个 函 数 Set(integer) , 该 祖 先 窗 口 的 子 孙 对 象 为

w_a_desc,子孙对象重载 Set(integer)函数,在子孙对象中重构了 Set(string)函数。

第 1 种情况:

w_a mywindow

open(mywindow)

当用户对 Set()函数进行静态和动态调用的时候,会出现以下情况。

mywindow.set(1):编译正确,执行的时候祖先对象的 set 函数被执行。

mywindow.set(“String”):编译错误,因为在 w_a 对象中不存在参数类型为字符串

类型的 set 函数。

mywindow.DYNAMIC set(“String”):编译正确,因为使用了动态调用隐藏了编译错

误,但在执行过程中会出现错误,因为在 w_a 对象中不存在参数类型为字符串类型的 set

函数。

第 2 种情况:

w_a mywindow

open(mywindow, �w_a_desc�)

第 3 章 Pow erScript语言

— 65 —

当用户对 Set()函数进行静态调用或动态调用的时候,会出现以下情况。

mywindow.set(1):编译正确,因为祖先对象 w_a 中有 Set(integer)函数,但在执行

时,是执行子孙对象的 Set 函数。

mywindow.set(“String”):编译错误,因为在 w_a 对象中不存在参数类型为字符串

类型的 set 函数。

mywindow.DYNAMIC set(“String”):编译正确,因为使用了动态调用隐藏了编译错

误,在执行过程中执行子孙对象中定义的 Set(string)函数。

动态调用有以下缺点。

(1) 降低程序的执行性能。由于动态调用与静态调用不同,它在执行的时候才寻找被

调用的函数,所以动态调用比静态调用速度慢。如果应用程序的执行速度很关键,那么就

要避免使用动态调用。

(2) 程序容易出错。使用动态调用时,编译器不检查动态函数或事件的存在和匹配,

事实上削弱了编译器的语法检查的功能。除非不得已,否则,建议不要使用函数的动态调

用方式。

3.6.2 结构

结构是一个或多个变量用同一组名所组成的集合。这些变量可以是任何数据类型,包

括标准数据类型、对象数据类型和其他的结构类型。例如一个单位中的人事档案数据可表

述为一个结构,其中的数据有职工号、姓名、出生年月、工作时间、工资等,这些数据有

不同的类型。

1. 定义

在结构画板或诸如窗口、菜单或 User Object Painter(用户对象画板)中可定义一个结构

类型。结构必须在声明后再使用,选择菜单 Insert 的子菜单 Struct 后,将会弹出一个结构

定义子窗口。声明后,它的一个实例自动被创建,与此对应,当它超过作用域时,该结构

自动被删除。

2. 声明结构

如果已在结构画板中定义了一个如图 3-6 所示名为 str_emp_data 的结构,可以在脚本

或一个对象的实例变量中声明它的一个实例。

声明该结构 str_emp_data 的两个实例变量的

语句如下:

str_emp_data str_emp1, str_emp2

3. 访问与赋值

在脚本中,用点操作符来访问结构内的变量。语法如下:

structurename.variable

点前为结构名,点后为结构的变量名。

如下语句给变量 str_emop_data 内的变量赋值:

图 3-6 用 Structure 画板创建结构

Pow erBuilder9.0 基础应用与系统开发

— 66 —

str_emp1.emp_id = 100

str_emp1.emp_lname = "张三"

str_emp1.emp_salary = 200

str_emp2.emp_id = 101

str_emp2.emp_salary = str_emp1.salary * 1.05

如果结构被声明为一个对象的一部分时,能用点操作符访问该结构变量,语法为:

objectname.structurename.variable

假定如下声明作为窗口 w_customer 的实例变量:

str_cust_data str_cust1

在对象内部,可用如下语句访问该结构变量(代词 This 可选):

This.str_cust1.name

在其他对象中,用如下的语句来访问该结构中的变量:

w_customer.str_cust1.name

3.7 系统对象

在 PowerBuilder 中,提供了几个全局可用的系统对象,这些对象对跟踪和了解程序

的运行状态和控制程序运行起着重要的作用,下面主要介绍 Error 和 Message 对象。

3.7.1 Error

Error 对象用来报告所有运行时的错误,用户可以在代码中访问 Error 对象以了解错误

发生的类型,其中典型的例子是 SystemError 对象。用户还可以对系统提供的 Error 对象

进行继承,来创建自己的错误处理对象。

Error 对象的属性见表 3-11。

表 3-11 Error 对象

属 性 数据类型 描 述 Number Integer 错误号 Text String 错误消息文本 WindowMenu String 发生错误的窗口或菜单 Object String 发生错误的对象 ObjectEvent String 发生错误的事件 Line Integer 发生错误的代码行

有一些错误可以在应用程序中俘获,这类错误均是致命错误(Fatal Error),如果不在

SystemError 事件中进行处理,将导致应用程序的自动退出。

例如:在应用程序对象的 SystemError 事件中编写如下代码:

MessageBox(“错误”, “错误号:”+string(error.Number)+” 原因:”+ error.text)

第 3 章 Pow erScript语言

— 67 —

当发生除零错误时,该事件俘获该错误,弹出如图 3-7 所示的错误消息框,并自动退出。

图 3-7 错误消息

如果不在应用对象的 SystemError 事件中处理,当发生除零错误时,系统弹出自己的

错误消息框,如图 3-8 所示。

图 3-8 系统自己的错误消息框

表 3-12 列出了可以在应用程序对象的 SystemError 事件中俘获的系统错误。

表 3-12 可以在 SystemError 事件中俘获的系统错误

错误号 错误内容 错误号 错误内容 1 被零除 22 不可知对象类型 2 空对象引用 23 不能将对象类型赋值给对象变量 3 数组越界 24 函数调用格式与定义格式不符 4 枚举类型越界 25 浮点数越界 5 函数中出现错误的赋值 26 字段赋值错误 6 对数据窗口中不存在的行或列引用 27 指数运算错误 7 调用外部函数错误 28 VBX 调用错误 8 通过 NULL 引用数组 30 引用不支持的外部对象数据类型 9 调用不存在的 DLL 函数 31 外部对象数据类型错误

10 为 DLL 函数传递不支持的参数类型 32 调用不存在的外部函数 11 数据窗口的列和函数 GetItem()不符 33 调用外部函数时参数不匹配 13 属性引用错误 34 调用外部函数时参数个数不匹配 14 打开 DLL 库错误 35 调用外部对象属性错误 15 调用错误的外部函数 36 调用不存在的外部对象属性名 16 超过字符串的最大界限 37 调用不匹配的外部对象数据类型 17 数据窗口对象引用不存在的数据窗口 38 调用外部对象错误 18 函数无返回值 39 调用外部对象属性错误 19 使用 Any 数据类型发生转换错误 40 使用 Any 数据类型不匹配 20 数据库命令错误 41 错误的 Any 数据类型 21 运行时(runtime)函数错误引用 42 调用 DLL 函数时参数不匹配

有 10 种错误不会触发应用程序对象的 SystemError 事件,它们将使应用程序立即中

止,这 10 种错误见表 3-13。

表 3-13 不会触发 SystemError 事件的错误

错误号 错误内容 错误号 错误内容 50 应用程序引用错误 55 在执行函数 Yield()时窗口关闭 51 装载动态库失败 56 数据库界面不支持远程调用 52 丢失祖先类 57 数据库界面不支持数组变量 53 丢失祖先对象 58 Blob 对象使用错误 54 祖先对象命名冲突 59 超越最大值

Pow erBuilder9.0 基础应用与系统开发

— 68 —

在 PowerBuilder 中提供了两个函数进行用户自己的错误处理。

(1) SignalError({number},{text}):该函数触发应用程序对象的 SystemError 事件。

其中,number(可选),为整型数据类型,保存错误消息的编号。

Text(可选),为字符串数据类型,保存错误消息中使用的描述文本。

该函数返回值为整型,如果函数调用成功返回值为 1,否则为-1。

(2) PopulateError(number, text):该函数填充错误对象(Error Object)的具体数据,包括

错误号和错误描述文本,但不触发应用程序对象的 SystemError 事件,可以与 SignalError()

函数联合使用。

其中,number(可选)为整型,保存错误消息的编号。

Text(可选),为字符串型,保存错误消息中使用的描述文本。

该函数返回值为整型,如果函数调用成功返回值为 1,否则为-1。

在程序设计过程中,可以使用这两个函数设计一些错误陷阱(Error trap)。如果用户进

行了一些不合法的操作,可以触发错误陷阱,在用户更改错误之前,程序不进行下一步处

理。设计错误陷阱时要注意,必须在应用程序对象 SystemError 事件中加入错误处理代

码,否则使用函数 SignalError 不会产生任何作用。在触发错误陷阱的同时,还可以通过

一个通用的错误消息窗口来通知用户错误发生的原因和位置。

3.7.2 Message

消息对象(Message Object)用来处理非 PowerBuilder 本身的事件,它主要和 Windows

系统和其他 GUI(Graphics user interface)环境进行消息通信。

PowerBuilder 已经将大量的 Windows 消息映射为自身的事件,但是偶尔也必须捕捉

一条未被映射的消息,该代码通常被放在对象的 Other 事件中进行处理,用户应该慎重使

用 Other 事件,因为系统将每一条未被映射的消息均发送给该事件,从而影响系统性能。

消息对象也用于窗口打开和关闭时传递额外的参数,有如下函数可用。

OpenWithParm( 打 开 窗 口 ) 、 OpenUserObjectWithParm( 打 开 用 户 对 象 窗 口 ) 、

OpenSheetWithParm(打开工作表窗口)、CloseWithParm(关闭窗口)和 OpenUserObject(打开

用户对象)。这些函数的作用大同小异,只是应用场合不同,下面以 OpenWithParm 函数

为例说明这类函数的用法。

OpenWithParm 函数的语法如下:

OpenWithParm ( 窗口变量, 参数 {, parent } )

该函数打开窗口,并传递参数给被打开的窗口。下面语句是打开名为 w_employee 窗

口的一个实例,并把字符串型参数存储在 Message.StringParm 中:

OpenWithParm(w_employee, "James Newton")

下列语句把窗口的 Open 事件传递的参数赋给静态文本控件 st_empname:

st_empname.Text = Message.StringParm

消息对象有 9 个属性,其中 4 个直接映射到 Windows 消息结构,其具体属性见表 3-14。

第 3 章 Pow erScript语言

— 69 —

表 3-14 Message 对象的属性

属 性 数据类型 描 述 Handle Integer 控件、窗口句柄 Number Integer 事件 ID Wordparm Unsignedint 字参数 Longparm Long 长整型参数 Doubleparm Double 数字或数值变量 Stringparm String 字符或字符串变量 Powerobjectparm Powerobject 任意的 PowerBuilder 对象 Proxyname String 应用代理对象 Processed Boolean 是否调用 DefWindowProc

Windows 的消息被定义为如下结构:

Typedef struct tagMsg{

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

}MSG;

与 Windows 消息相比,PowerBuilder 中定义的消息更加全面。

为了得到合适的消息 ID 号,用户可以在 Windows.h 头文件中查找,只要有了正确的

ID 号,就可以在对象的 Other 事件中俘获该消息。用户也可以自定义用户事件,所以可

以不再使用 Other 事件。

在事件子窗口中定义自己的事件,如图 3-9 所示。图中为窗口 w_jzh_w1 定义了一个

自定义事件 CusorPos,返回值为布尔型,带名为 row 和 col 的两个整型值传递的参数。事

件中,调用了系统预定义函数 MessageBox,用于产生一个消息框,提示当前光标位置。

图 3-9 自定义事件窗口

通过下列的代码触发该事件:

parent.trigger event CursorPos(200,300) //传递给参数 row 和 col 地值分别为 200

和 300。

Pow erBuilder9.0 基础应用与系统开发

— 70 —

3.8 命名规则

从理论上讲,只要满足标识符取名规则的标识符都可以作为类型名、变量名、对象名

和控件名。但是,只有按照某种约定,写出的程序才容易被人看懂,方便调试和维护。

标识符命名规则的精髓是命名的标识符要见名知义。也就是说,当看到该标识符时,

即使你没有看它的定义,也知道它的作用、作用域和类型,若它是标识控件的,知道它是

哪一类控件。

在不同的编程语言中,标识符的命名规则具有共同性,但是由于编程环境的作用和特

性不同,命名规则也具有其个性。下面是在 PowerBuilder 中关于命名规则的一些建议。

(1) 对于作用域,用字符 g 表示全局变量,用字符 i 表示实例变量,用字符 l 表示局

部变量,用字符 s 表示共享变量。

(2) 对于数据类型或控件类型,代表字符见表 3-15。

表 3-15 数据类型和控件类型命名表

数据类型或控件名 代表字符 数据类型或控件名 代表字符 窗口 Window W 多行编辑 MultiLineEdit mle

数据窗口 DataWindow D 丰富编辑控制 RichEditControl rec

数据窗口控件 DataWindowControl Dw 水平滚动条 HscorllBar hsb

菜单 Menu m 垂直滚动条 VscorllBar vsb

命令按钮 Command Button cb 垂直轨迹条 VtrackBar vtb

图形按钮 PictureButton pb 水平进度条 HprogressBar hpb

检测框 CheckBox cbx 垂直进度条 VprogressBar vpb

单选按钮 RadioButton rb 下拉列表框 DropDownListBox ddlb

静态文本 StaticText st 下拉图像框 DropDownPictListBox ddplb

静态超文本连接 StaticHyperLink shl 列表框 ListBox lb

图像 Picture p 图像列表框 PictureListBox plb

图像超文本连接 PictureHyperLink phl 表型预览 ListView lv

成组框 Group Box gb 树型预览 TreeView tv

线 Line l 标签 Tab tab

椭圆 Ovel ov 图形 Graph gr

矩形 Rectangle r OLE 对象 OLE objects ole

圆角矩形 Round Rectangle rr 水平轨迹条 HtrackBar htb

单行编辑 SingleLineEdit sle 编辑掩码 EditMask em

字串型 String s 字符型 char/character c

整型 integer/int i 日期时间型 datetime dt

日期型 Date dt 时间型 time t

布尔型 Boolean b 小数型 Decimal/dec d

双精度型 Double db 无符号整数型

Unsignedinteger/unsignedint u

(3) 变量的具体名称要见名知义,一般用英语单词、汉语拼音或其简写。

(4) 对于窗口的控件,不需要作用域指示,所以它的一个完整的标识符由控件类型、

下划线和变量的具体名组成。例如:

w_genapp_frame 代表应用程序 genapp 的框架窗口

cb_ok 代表确定按钮

em_nj 代表输入和显示学生年级的掩码编辑器

第 3 章 Pow erScript语言

— 71 —

对于变量的命名,要有作用域指示,所以它的一个完整的标识符,由变量作用域、数

据类型、下划线和变量的具体名组成。例如:

integer ii_count 定义整型用于计数的实例变量

string ls_temp_stu_name 定义字符型用于临时存储学生姓名的局部变量

boolean gb_error = TRUE 定义布尔型用于表示应用程序是否有错误的全程变量,并赋值为

真。

总之,按一种大家熟知的命名方法,可以提高程序的可读性,便于调试和维护。以上

建议供读者参考。

3.9 数据库支持

在 PowerScript 的脚本中支持嵌入式 SQL 语句和动态的 SQL 语句。PowerScript 支持

与特定数据库相关的语句和保留字,例如:在 PowerBuilder 中使用 Select 语句(SQL 的选

择语句,后节将介绍)时,就可以使用该数据库提供的所有函数。

在 PowerScript 里的 SQL 语句中,可以使用 PowerScript 中定义的常量,若要使用

PowerScript 中定义的变量,在 SQL 语句中,变量名前必须加一个冒号(:),在 SQL 语句中

使用的变量称为宿主变量。例如:

select name, sex, age, score from students where sex = �男� and age>: ll_minage;

上述语句中 ll_minage 是在 PowerScript 中定义使用的宿主变量。

3.9.1 PowerBuilder 中嵌入式 SQL 语句

SQL 语言是通用关系型数据库(如 Sybase,IBM 的 DB2,微软的 SQL Server,Oracle 及

Informix)的结构化查询语言,用于操纵数据库。如建立数据库表、插入记录、删除记录、

建立索引和对数据库对象的授权等。

各个数据库厂商提供的 SQL 语言集合都遵循 ANSI(American National Standards

Institute,美国国家标准组织)标准,在 ANSI 文档 X3.135-1992 Database Language SQL 中

对 SQL 语言进行了定义。该标准(即通常所知的 SQL92 或 SQL2)有 3 个结构层次:

Entry(处级)、Intermediate(中间级)以及 Full(完全级)。各具体数据库支持哪一级,请查阅

数据库的技术资料。同时各数据库对该标准进行了必要的扩充。

用 SQL 语言来操纵数据库的好处是:SQL 语句是非过程语句,且语句易懂;使用

SQL 语句使程序具有较好的可移植性,可以轻松地把应用程序从依赖的一种数据库(如

DB2)移植到另一种数据库(如 Oracle)中。

PowerScript 语句是可以在 PowerBuilder 脚本中使用的 SQL 语句。在 PowerScript 脚

本的执行过程中,当控制到达这些 SQL 语句时,这些 SQL 语句将会送入指定的数据库服

务器执行,并返回所需的结果,控制转到下一条语句。

下面分别介绍各嵌入式 SQL 语句。

Pow erBuilder9.0 基础应用与系统开发

— 72 —

1. SELECT 语句

SELECT 语句用于在指定的表(可能是多个表)里根据条件选择一行。其语法为:

SELECT RestOfSelectStatement {USING TransactionObject} ;

参 数 TransactionObject 为 被 操 作 数 据 库 的 TransactionObject 对 象 名 。

RestOfSelectStatement 是 SELECT 语句的后面部分,一般会有字段列表部分指定查询哪些

字段,Into 子句把返回的字段值存入宿主变量表中,From 子句指明所检索的数据库表或

视图,Where 子句指定检索条件,Group 子句用于分组,Order by 子句用于对检索结果排

序。

通过测试 TransactionObject 对象的 SQLCode 属性进行错误处理。

注意若 SELECT 语句返回多于一行,程序将会出错。当在该语句中使用了 INTO 子

句时,PowerBuilder 并不检查被检索的列和对应的宿主变量的类型是否匹配。它仅检查表

和列的存在性,用户有必要检查类型的匹配性和对应数据类型的转化。

下文举出一例,该例先从表 STU 中取出一数据行,然后用消息框显示出来。程序段

如下:

string ls_name

char lc_sex

decimal ld_age

decimal ld_score

SELECT STU.STU_NAME,

STU.STU_SEX,

STU.STU_AGE,

STU.STU_SCORE

INTO

:ls_name, :lc_sex, :ld_age, :ld_score

FROM STU

where STU.ID = 100001

using SQLCA;

IF SQLCA .SQLCode = 100 THEN //错误检查

MessageBox("学生表查询", &

"学号为 100001 的学生不存在!")

ELSEIF Emp_tran.SQLCode>0 then

MessageBox("数据库错误", &

SQLCA.SQLErrText, Exclamation!)

END IF

messagebox("学生姓名", ls_name) // 显示数据给用户

2. DELETE 语句

该语句根据条件删除指定的表(可能是多个表)里的行(可能为多行)。语法为:

DELETE FROM TableName WHERE Criteria {USING TransactionObject} ;

其 中 参 数 TableName 为 要 删 除 数 据 行 的 表 名 ; Criteria 为 删 除 行 的 条 件 ;

TransactionObjec 指示数据库的 TransactionObject 对象名。

第 3 章 Pow erScript语言

— 73 —

通过测试 TransactionObject 对象的 SQLCode 属性,查看错误原因。若要看删除的行

数,查 TransactionObject 对象的 SQLNRows 属性。

下例是从表 STU 中删除 ID 号为 100001 的数据行的代码。

DELETE FROM STU

WHERE STU.ID = 100001 //删除条件

USING SQLCA;

IF SQLCA .SQLCode = 0 and SQLCA.SQLNRows>=0 THEN //错误检查

MessageBox("删除记录", "删除成功")

Commit using SQLCA; //提交,永久性更新数据库

ELSE

MessageBox("删除记录失败", SQLCA.SQLErrText, Exclamation!)

RollBack using SQLCA; //回滚,撤销对数据库的修改

END IF

3. UPDATE 语句

该语句在指定的表(可能是多个表)中,根据条件更新指定的行。语法为:

UPDATE TableName RestOfUpdateStatement

{USING TransactionObject} ;

其中参数 TableName 为需要更新的表的表名;RestOfUpdateStatement 为更新的条件

子句;TransactionObject 为被操作数据库的 TransactionObject 对象名。

通过测试 TransactionObject 对象的 SQLCode 属性,查看错误原因。若要看更新的行

数,查 TransactionObject 对象的 SQLNRows 属性。

下列是从表 stu 中更新 ID 号为 100001 的行, 把成绩更新为 99 的代码。

Long ll_stu_name

ll_stu_name = 100001

UPDATE STU

SET STU.STU_SCORE = 99

WHERE STU.ID = ll_stu_name

IF SQLCA.SQLNRows>0 THEN

COMMIT USING SQLCA ;

ELSE

ROLLBACK USING SQLCA;

MessageBox(“数据库更新失败”,SQLCA.SQLErrText, Exclamation!)

END IF

4. INSERT 语句

该语句用于向表中插入数据。由于各种关系数据库都提供多种数据类型,因此,当往

数据库中插入数据时,要特别注意不同数据类型的输入规则。语法如下:

INSERT RestOfInsertStatement {USING TransactionObject};

其中,RestOfInsertStatement 包括 INTO 子句、表中的列和准备插入的值。

例如,用下面的 INSERT 语句向表 STU 中添加一条记录:

Pow erBuilder9.0 基础应用与系统开发

— 74 —

INSERT INTO STU (ID, STU_NAME, STU_SEX, STU_AGE, STU_SCORE)

VALUES (100004, ‘李四’, 1, 23, 95);

上面列出的列名顺序与创建表时的列名顺序无关,但必须与给出的值的顺序相对应。

下面的语句与上面的语句产生同样的效果。

INSERT INTO STU (ID, STU_NAME, STU_SEX, STU_SCORE, STU_AGE)

VALUES (100004, ‘李四’, 1, 95, 23);

在 INSERT 语句中,也可以不给出列名,但要特别注意,给出的数据要与表结构中列

名的顺序一致,而且必须给出所有列的值。

由 于 表 STU 中 列 的 顺 序 为 ID 、 STU_NAME 、 STU_SEX 、 STU_AGE 和

STU_SCORE,所以下面的语句将与上面的语句产生同样的效果。

INSERT INTO STU VALUES(100004, ‘李四’, 1, 23, 95);

3.9.2 指示器变量

在 PowerBuilder 中支持指示器变量。所谓指示器变量是指用于数据库检索之后反应

某字段得到的值是否为空值或检索是否有错误的变量。指示器变量是整型的,在 Select 和

Fetch 语句的 Into 子句中使用,而且放在宿主变量之后。宿主变量和指示器变量之间用空

格而不是逗号分隔。例如:

select name, address, city

into :Name, :Address, :City //不带指示器的宿主变量列表

from emp

where id=100001;

select name, address, city

into :Name :IndVar1, :Address :IndVar2, :City :IndVar3

//带指示器的宿主变量列表

from emp

where id=100001;

每一个指示器变量可能的取值见表 3-16。

表 3-16 指示器变量的取值及含义

值 代表的意义 0 对应的宿主变量得到非空的有效值 -1 对应的宿主变量得到空值 -2 对应的宿主变量得到的值发生转换错误

注意

不是所有的数据库都会报告字段和对应宿主变量可能遇到的数据转换错误。

下列例句的作用如语句后注释所示。

if IndVar2 = -1 then... //判断变量 Address 是否为空值

if IsNull( Address ) then ... //与上句等价的语句,但不用指示器变量

IndVar3 = -1 //用于把变量 City 置为空

SetNull( City ) //与上句等价的语句,但不用指示器变量

第 3 章 Pow erScript语言

— 75 —

3.9.3 SQL 语句的错误处理

上述语句并没有提供错误处理语句,在每个 SQL 语句执行之后,查看事务对象

(Transaction Object)的 SQLCode 属性以判断 SQL 语句执行的成败是一个好的做法。

SQLCode 的取值和意义见表 3-17。

表 3-17 SQLCode 的取值及其意义

SQLCode 值 意 义 0 成功

100 所取的行不存在 -1 错误,其细节见 Transaction Object 的 SQLErrText 和 SQLDBCode 属性

在 诸 如 Delete , Fetch 和 Update 等 SQL 语 句 执 行 之 后 , 应 检 查 事 务对 象 的

SQLNRows 属性,确保至少有一行起到作用。

SQLErrText 属性是数据库本身提供的错误消息,SQLDBCode 属性是数据库本身提供

的数据库状态代码。例如:

IF SQLCA.SQLCode = -1 THEN

MessageBox("SQL 错误", SQLCA.SQLErrText)

END IF

该段代码当检测到 SQL 语句执行失败后,显示相应的错误提示文本。

3.9.4 事务管理语句

事务是关系数据库管理系统提供的一种机制,它确保一个或多个语句被当成单一工作

单元来处理。事务的特点是要么工作单元内所有的操作都成功,要么都失败,这对保证数

据库系统中数据的一致性非常重要。

PowerBuilder 对事务的管理方式与一般关系数据库(如 Sybase 或 Oracle)的管理方式相

同。PowerBuilder 有 4 条事务处理语句,分别为 CONNECT、DISCONNECT、COMMIT

和 ROLLBACK。

在一个事务中可以用如下的 SQL 语句。

(1) 声明 SQL 游标和存储过程。

(2) 使用游标的 FETCH,UPDATE 和 DELETE 语句。

(3) 非游标的 SELECT,INSERT,UPDATE 和 DELETE 语句。

关于存储过程和游标的定义和使用请参见 3.9.5 和 3.9.6 节。

1. CONNECT 语句

CONNECT 语句是连接到指定的数据库的语句,语法为:

CONNECT {USING TransactionObject};

参数 TransactionObject 为该命令所使用的事务对象,包含了想连接的数据库所需的连

接信息。只有当连接信息不是默认值 SQLCA,才需要指出这个参数。

该语句必须在任何 SQL 语句执行之前执行,目的是指明要对哪一个数据库进行操

Pow erBuilder9.0 基础应用与系统开发

— 76 —

作。该语句一般在应用程序对象的 open 事件中执行。

可以在 CONNECT 语句执行之后进行错误处理,来测试连接是否成功。

例如用默认的 transaction object 连接到数据库的语句为:

CONNECT ;

用名为 O8iBegin 的 transaction object 连接数据库的语句为:

CONNECT USING O8iBegin ;

2. COMMIT 语句

COMMIT 语句是事务提交语句,它对 transactionobject 对象所指定的数据库的前一个

ROLLBACK、COMMIT 或 CONNECT 语句之后对数据库所做的修改进行永久性的更新。

语法为:

COMMIT {USING TransactionObject} ;

参数 TransactionObject 指示要操作的数据库的事务对象名。默认时为 SQLCA。

COMMIT 语句并不会断开数据库连接,但是会关闭所有打开的游标和存储过程。

注意

执行 DISCONNECT 语句时会自动发出一条提交命令 COMMIT。

可以通过测试 TransactionObject 对象的 SQLCode 属性,查看错误原因进行错误处理。

例如对事务对象 SQLCA 进行提交的语句为:

COMMIT ; //用默认的事务对象 SQLCA

对名为的 O8iBegin 事物对象进行提交的语句为:

COMMIT USING O8iBegin ;

3. ROLLBACK 语句

ROLLBACK 语句是事务回滚语句,撤销对 transaction object 对象所指定的数据库的

前一个 ROLLBACK、COMMIT 或 CONNECT 语句之后对数据库所做的修改。参数和用

法类同于 COMMIT 语句。语法为:

ROLLBACK {USING TransactionObject} ;

例如对事务对象 SQLCA 进行回滚可用下述语句:

ROLLBACK ; //用默认的事务对象 SQLCA

对名为的 O8iBegin 事务对象进行回滚用下述语句:

ROLLBACK USING O8iBegin ;

4. DISCONNECT 语句

DISCONNECT 语句是断开数据库连接语句,断开对 transaction object 对象所指定的

第 3 章 Pow erScript语言

— 77 —

数据库的连接。执行该语句时,先发送一条数据库提交命令 COMMIT,然后断开连接。

语法为:

DISCONNECT {USING TransactionObject} ;

例如断开事务对象 SQLCA 所指数据库的连接语句为:。

DISCONNECT;

断开对名为 O8iBegin 的事务对象所指数据库的连接语句为:

DISCONNECT USING O8iBegin ;

3.9.5 利用存储过程操作数据

查询多行数据可以用存储过程来完成,存储过程是多条语句的集合。注意并非所有的

DBMS 都支持存储过程。

1. 创建存储过程

在 PowerBuilder 中能通过动态 SQL 或在 DataBase 画板下的 SQL 语句的执行平台上

创建存储过程。也可以在数据库系统提供的工具上创建存储过程,例如,对 Oracle,可用

SQL*PLUS;对 MS SQL SERVER,可用它提供的企业管理器。另外,由于 DBMS 的不

同,存储过程的语法也不一样。例如,下面是在 SQL Anywhere 和 Oracle 下定义的两个功

能完全一样的存储过程:

CREATE PROCEDURE porc_anywhere /*创建 SQLAnywhere 的存储过程*/

in @radii decimal(2,0), /*定义传入参数*/

out @area decimal(7, 2) /*定义传出参数*/

AS

Begin

Select @area = 3.14*@radii*@radii /*赋值语句*/

End;

CREATE or REPLACE PROCEDURE porc_oracle /*创建 Oracle 的存储过程*/

( radii in number(2), /*定义传入参数*/

area out number(7, 2) ) /*定义传出参数*/

AS

Begin

area := 3.14*radii*radii; /*赋值语句*/

End;

从上面的例子可以看出,存储过程是由过程名、传入参数、传出参数和过程体等几部

分组成。存储过程在创建后,服务器对其进行编译并保存,供用户随时调用。由于存储过

程在创建时执行计划已经准备好,因此它的特点是执行速度快。关于各数据库定义存储过

程的语法,请参阅有关数据库的技术资料。

Pow erBuilder9.0 基础应用与系统开发

— 78 —

2. 在 PowerBuilder 中定义存储过程

在数据库中创建好存储过程后,就可以在 PowerScript 程序中定义存储过程了。

语法

DECLARE ProcedureName PROCEDURE FOR

StoredProcedureName

@Param1=Value1, @Param2=Value2,...

{USING TransactionObject} ;

参数

ProcedureName:任何合法的 PowerBuilder 标识符。

StoredProcedureName:任何存储在数据库中的存储过程名。

Paramn:该存储过程的参数 n 的参数名。

Valuen:一个有效的 PowerBuilder 表达式,代表了参数 n 的值。

TransactionObject:事务对象,同其他 SQL 语句。

下例声明一个 Oracle 的过程名 Get_Stu_Name,用默认的事务对象,它引用了变量

Stu_ID。在语句 EXECUTE Get_Stu_Name 之前对变量 Stu_ID 进行合适的赋值。

在 Oracle 8/8i 的 SQL*PLUS 中创建如下的存储过程:

Create or Replace Procedure GetName (

p_stu_id in number,

p_stu_name out varchar2(8)) as

begin

select stu_name into p_stu_name from stu

where id = p_stu_id;

end GetName;

/

在 PowerBuilder 中,声明如下的存储过程名:

DECLARE Get_Stu_Name procedure for GetName (:Stu_ID) ; //只能指定传入参数,

不能指定传出参数

3. 执行存储过程

执 行 被 定 义 好 的 存 储 过 程 , 使 用 的 存 储 过 程 名 是 DECLARE 语 句 中 的

ProcedureName。

语法

EXECUTE ProcedureName ;

其中,ProcedureName 为准备运行的存储过程名,该存储过程必须是已经定义好的。

这个 ProcedureName 不必与数据库中的存储过程同名。

例如执行标题 2 中已定义的过程:

第 3 章 Pow erScript语言

— 79 —

Stu_ID = 100001

EXECUTE Get_Stu_Name。

执行完存储过程后,服务器会把返回值放在该存储过程的结果集中,如果要将数据传

给程序使用,就需要用 FETCH 语句来提取结果集中的数据。

4. FETCH(取存储过程返回的数据)

执行存储过程后,存储过程返回的数据放在结果集中,通过 FETCH 语句可以提取结

果集中的数据。语法如下:

FETCH Procedure INTO HostVariableList ;

参数

Procedure:提取数据行的存储过程。

HostVariableList:宿主变量列表,宿主变量之间用逗号分隔,用于存放检索出的数

据。该语句一行一行提取被存储过程检索出的行。

错误处理:测试 TransactionObject 对象的 SQLCode 属性,查看是否成功,查看

TransactionObject 对象的 SQLNRows 属性的值确保至少提取了一行。

例如标题 3 中执行过程 Get_Stu_Name 的结果。

String Stu_Name

FETCH Get_Stu_Name INTO :Stu_Name ; //变量 Stu_name 中存储着学生姓名

5. CLOSE Procedure(关闭存储过程)

语法

CLOSE ProcedureName ;

参数

ProcedureName:在 PowerBuilder 中声明的存储过程名。

该语句用于关闭在 PowerBuilder 中声明的存储过程。只有用 EXECUTE 执行该存储

过程之后,才能用 CLOSE 语句。只有返回结果集的存储过程才需要用该语句关闭,无结

果集的过程在执行完成后将会自动关闭。CLOSE 语句一般在提取数据(FETCH)时返回值

为 100(无数据)之后使用。

错误处理:测试 TransactionObject 对象的 SQLCode 属性,查看是否成功。

例:关闭上例执行的存储过程 Get_Stu_Name。

CLOSE Get_Stu_Name ;

3.9.6 利用游标操作数据

SELECT 是描述型的语言,它面向的是集合,是一组记录。而 PowerBuilder 语言却是

面向过程的,它要一条一条地接收并处理记录。PowerScript 通过描述型游标(CURSOR)在

这组记录上游动的方法,给 Script 语句逐个传送记录,建立集合和记录之间的内在联系。

Pow erBuilder9.0 基础应用与系统开发

— 80 —

游标是通过声明与 SELECT 语句关联的符号名,游标可以单独处理 select 语句返回的每一

行。游标包括两部分内容。

(1) 结果集:游标内 SELECT 语句执行后产生的数据集合。

(2) 游标位置:游标指针的当前位置。

游标实际上是利用它内部的 SELECT 语句把数据放到它的结果集中,然后通过移动

它的指针来逐条访问这些数据。在游标中,SELECT 语句决定了要存取记录的集合,规定

了记录的选择和顺序。PowerScript 的游标控制着这个集合。向下移动游标指针即可将这

些记录逐个取出,送给 PowerScript 语句加工。通过引进游标,将得到的数据逐个传给

PowerScript 程序。因此,游标是架设在一组记录 (SQL 查询结果 )和单个记录 (每次

PowerScript 语言能处理的内容)之间的一座桥梁。

游标定义和使用的步骤如下。

(1) 声明游标(declare cursor)。

(2) 打开游标(open cursor)。

(3) 推进游标(fetch row)。

(4) 通过游标访问和修改数据。

(5) 关闭游标(close cursor)。

1. DECLARE Cursor(定义游标)

用于为指定的事务对象定义游标。语法如下:

DECLARE CursorName CURSOR FOR SelectStatement

{USING TransactionObject} ;

参数

CursorName:要定义的游标名。

SelectStatement:游标中使用的合法的 SELECT 语句。

TransactionObject:事务对象名,默认为 SQLCA。

该语句和声明变量类似,是非执行语句,必须用 OPEN、FETCH 和 CLOSE 语句对游

标变量进行操作。

例:生成一个处理学生记录的游标 stu_cur。

DECLAREe stu_cur CURSOR FOR

SELECT STU.STU_NAME,

STU.STU_SEX,

STU.STU_AGE,

STU.STU_SCORE

FROM STU

ORDER BY stu.id asc

USING SQLCA;

2. OPEN Cursor(打开游标)

当执行了 Open Cursor 语句后,可以用 FETCH 语句从记录集中取出一条记录,如果

第 3 章 Pow erScript语言

— 81 —

记录集中有多条记录,应该用多条 FETCH 语句提取数据。当游标使用完毕后,必须用

Close Cursor 语句关闭该游标。语法如下:

OPEN CursorName ;

参数

CursorName:要打开的游标名。

注意

该语句不需要 USING TransactionObject 子句,因为 USING TransactionObject 子句已

在定义游标时使用过。

3. FETCH ROW(推进游标)

推进游标也就是移动游标指针,改变游标结果的当前记录。语法如下:

FETCH Cursor INTO HostVariableList;

其中 Cursor 是游标名,HostVariableList 是宿主变量,即 PowerScript 中的变量,用来

获取游标返回的数据。

该语句的功能是把结果集中的当前记录取出来,放入宿主变量列表中,然后把游标指

针移到下一条记录。

如果用户使用的 DBMS 支持 FETCH 的其他格式(这里默认格式是 FETCH NEXT,把

游标指针推向下一条记录),可以使用 FETCH FIRST、FETCH PRIOR 或 FETCH LAST 等

FETCH 语句的格式。

4. CLOSE Cursor(关闭游标)

其作用是关闭打开的游标,结束对游标的处理。该语句一般用于游标中的数据提取完

毕之后,或结果集中的数据不需要再提取时。判断游标中的记录是否提取完,可检查事务

对象的 SQLCode 属性的值是否为 100。关闭的游标可再次打开。

语法

CLOSE CursorName ;

参数

CursorName:已打开的游标名。

例:打开上一小节定义的游标 stu_cur,并从此游标中取出前三行。

String ls_stu_name

Decimal ld_stu_sex

Decimal ld_stu_age

Decimal ld_stu_score

String ls_stu_sex

OPEN stu_cur ; //打开游标

Decimal ld_I

Pow erBuilder9.0 基础应用与系统开发

— 82 —

//在结果集中提取一行

FETCH stu_cur INTO :ls_stu_name,:ld_stu_sex,:ld_stu_age,:ld_stu_score;

For ld_i=1 to 3

If SQLCA. SQLCode = 0 and SQLCA.SQLNRows>0 then //提取到数据吗?

If ld_stu_sex = 1 then

ls_stu_sex = "男"

else if ld_stu_sex = 0 then

ls_stu_sex = "女"

end if

MessageBox("学生信息", "学生姓名:"+ ls_stu_name+&

"~n" + "学生性别:"+ ls_stu_sex +&

"~n" + "年龄:" + string(ld_stu_age)+&

"~n" + "成绩:" + string(ld_stu_score))

//在结果集中提取下一行

FETCH stu_cur &

INTO :ls_stu_name,:ld_stu_sex,:ld_stu_age,:ld_stu_score;

End if

Next

Close stu_cur; //关闭游标

3.9.7 使用动态的 SQL

前面讲到的在 PowerBuilder 中使用的 SQL 语句都是确定的。当编译 Script 脚本程序

时,整个 SQL 语句都是已知的,它们不能在运行时动态改变。但是,在很多情况下,

SQL 语句或 SQL 语句所带的参数在编译时并不知道,应用程序必须在运行时才能生成

SQL 语句。这种在程序运行时才生成的 SQL 语句称为动态 SQL 语句。在 PowerBuilder 中

有以下 4 种动态生成 SQL 的执行方式。

(1) Format1:不带传入参数,没有返回结果的 SQL 语句,这种语句可用来执行数据

定义语句(DDL)。

(2) Format2:有传入参数,没有返回结果的 SQL 语句,这种语句可用来执行所有形

式的数据定义语句。

(3) Format3:在编译时已经知道传入参数和结果集中列出的 SQL 语句。

(4) Format4:在编译阶段,既不知道传入参数,也不知道结果集中列出的 SQL 语

句。

下面首先讲解动态 SQL 的基础知识。

3.9.7.1 动态 SQL 的基础知识

PowerBuilder 的动态 SQL 语句,包括 CLOSE、DECLARE、FETCH、OPEN 和

EXECUTE 等语句的动态形式。与动态 SQL 有关的还有 PowerBuilder 中的两个数据类

型:DynamicStagingArea 和 DynamicDescriptionArea。

1. PowerBuilder 中的动态 SQL 语句

DISCRIBE DynamicStagingArea INTO DynamicDescriptionArea ;

EXECUTE {IMMEDIATE} SQLStatement {USING TransactionObject} ;

第 3 章 Pow erScript语言

— 83 —

EXECUTE DynamicStagingArea USING ParameterList ;

EXECUTE DYNAMIC Cursor|Procedure {USING ParameterList} ;

OPEN DYNAMIC Cursor|Procedure USING DISCRIPTOR ParameterList ;

EXECUTE DYNAMIC Cursor|Procedure USING DISCRIPTOR

DynamicDescriptionArea ;

OPEN DYNAMIC Cursor|Procedure USING DISCRIPTOR DynamicDescriptionArea ;

PREPARE DynamicStagingArea FROM SQLStatement {USING TransactionObject} ;

2. 与动态 SQL 相关的数据类型

(1) DynamicStagingArea 是 PowerBuilder 中的一种数据类型。PowerBuilder 使用该数

据类型的变量为后继的动态 SQL 语句保存信息。DynamicStagingArea 用做语句的运行和

事务对象之间的连接,用户不能访问 DynamicStagingArea 中的数据。

PowerBuilder 中提供了一个全局的 DynamicStagingArea 型变量,变量名为 SQLSA,

当需要一个 DynamicStagingArea 型变量时可以使用它。

如果需要,可以定义自己的 DynamicStagingArea 类型的变量。当动态 SQL 语句中使

用这个变量时,必须创建和定义这个变量。下面是定义和创建这个变量的语句:

DynamicStagingArea dsa_1

Dsa_1 = CREATE DynamicStagingArea

当 EXECUTE 语句执行后,SQLSA 就不再被使用。

(2) DynamicDescriptionArea 是 PowerBuilder 的另一种数据类型。PoweBuilder 使用这

种类型的变量保存用在 Format4 格式的动态 SQL 语句的传入和传出参数的信息。

PowerBuilder 中提供了一个 DynamicDescriptionArea 类型的全局变量 SQLDA,用在

需要使用 DynamicDescriptionArea 变量的时候。如果需要的话,也可以定义和创建另外的

DynamicDescriptionArea 类型的变量,在使用时一定要先定义并创建它。例如:

DynamicDescriptionArea dda_1

dda1 = CREATE DynamicDescriptionArea

3. 使用动态 SQL 的准备

在使用动态 SQL 时,必须注意以下几点。

(1) 首先要为除 Format1 以外的其他方式准备好 DynamicStagingArea 型变量。

(2) 定义并创建 Format4 的 DynamicDescriptionArea 变量。

(3) 按动态 SQL 正确的执行顺序执行。

4. 准备并描述数据类型

可 以 用 系 统 预 定 义 的 变 量 SQLSA 和 SQLDA , 也 可 以 定 义 和 创 建 其 他 的

DynamicStagingArea 和 DynamicDescriptionArea 型变量。

下面是一个动态游标的定义和打开:

DECLARE stu_cur1 DYNAMIC CURSOR FOR SQLSA;

//下面的 PREPARE 语句必不可少,否则将会出现执行错误

PREPARE SQLSA FROM “Select stu_name, stu_sex From stu”; //准备 SQL 语句

OPEN DYNAMIC my_cur1;

Pow erBuilder9.0 基础应用与系统开发

— 84 —

5. 语句顺序

动态 SQL 语句的顺序是很重要的,在 Format2、Format3 和 Format4 中的执行顺序如

下。

(1) DECLARE 和 PREPARE 语句必须在其他的动态 SQL 语句之前执行。

(2) Format3 和 Format4 中的 OPEN 语句必须在 FETCH 语句前执行。

(3) CLOSE 语句必须在最后执行。

注意 PREPARE 语句会因 From 子句内容影响 SQLSA 中的内容。在下面的语句中,

当遇到不同数据时,SQLSA 中的内容是不同的:

DECLARE stu_cur1 DYNAMIC CURSOR FOR SQLSA;

Integer sex

String Sql1, Sql2

sex=1

Sql1 = �Select stu_name, stu_age, stu_score FROM stu WHERE stu_sex=1� Sql2 = �Select stu_name, stu_age, stu_score FROM stu WHERE stu_sex=0� IF sex = 1 THEN //当 sex=1 时,用 Sql1 的 PREPARE 语句 PREPARE SQLSA FROM :Sql1 USING SQLCA; ELSE //当 sex=1 时,用 Sql2 的 PREPARE 语句 PREPARE SQLSA FROM :Sql2 USING SQLCA; END IF OPEN DYNAMIC stu_cur1;

3.9.7.2 Dynamic SQL Format1(动态 SQL 格式 1)

使用这种格式执行的 SQL 语句不产生结果集,并且不需要输入参数。可以用这种格

式执行所有的数据定义语言。语法如下:

EXECUTE IMMEDIATE SQLStatement {USING TransactionObject};

参数

SQLStatement:包含合法 SQL 语句的字符串。

TransactionObject:被操作的数据库的事务对象名。

注意

语句中的参数 SQLStatement 可以使用除 SELECT 之外的语句,且 SQLStatement 中

SQL 语句不能包含变量。

例:下面语句创建一个数据库表 STU 的索引,它用串变量 ls_string1 存储 SQL 语

句。

string ls_string1 ls_string1 = "CREATE INDEX stu_score_index ON stu(stu_score)" EXECUTE IMMEDIATE :ls_string1;

第 3 章 Pow erScript语言

— 85 —

注意

若与 MS SQLServer、Sybase 11、ODBC 数据库连接,在运行上面的 EXECUTE 语句

之前,应置事务对象属性 AutoCommit 为 TRUE。但是,这样会破坏正常的事务处理。

与上面等价的语句如下:

EXECUTE IMMEDIATE "CREATE INDEX stu_score_index ON stu(stu_score)";

下面语句在数据库中删除一条记录:

String ls_string1

ls_string1 = �delete from stu where id = 100001� EXECUTE IMMEDIATE :ls_string1 ;

用下面语句在数据库上创建一个表 class:

string ls_string1 ls_string1 = �CREATE TABLE class �+& �(class_id number(6) not null, �+& �class_name varchar2(26) not null)� EXECUTE IMMEDIATE :ls_string1;

使用 format1 格式的动态 SQL 语句适合于在程序运行期间,创建一些简单、临时的

数据库对象。

3.9.7.3 Dynamic SQL Format2(动态 SQL 格式 2)

使用这种格式执行的 SQL 语句不产生结果集,但需要传入参数。可以用这种格式执

行所有的数据定义语言。语法如下:

PREPARE DynamicStagingArea FROM SQLStatement {USING TransactionObject}; EXECUTE DynamicStagingArea USING {ParameterList} ;

参数

DynamicStagingArea:DynamicStagingArea 对象名。

SQLStatement:包含合法 SQL 语句的字符串。

TransactionObject:被操作的数据库的事务对象名。

ParameterList:用逗号分隔的一组 PowerScript 变量表,在它们的前面要有冒号。

注意

语句中的参数 SQLStatement 可以使用除 SELECT 之外的语句,且 SQLStatement 中

SQL 语句可包含参数,在参数 ParameterList 中使用的变量要预先声明。

例如下面语句在一个数据库表 STU 中插入一条记录:

long ll_stuid, ll_stusex, ll_stuage, ll_stuscore string ls_stuname string ls_string1

Pow erBuilder9.0 基础应用与系统开发

— 86 —

ls_string1 = "insert into stu "+&

"(id,stu_name,stu_sex,stu_age,stu_score) "+&

"values (?,?,?,?,?)"

PREPARE SQLSA FROM :ls_string1;

ll_stuid = 100001

ll_stusex = 1

ll_stuage = 25

ll_stuscore = 94

ls_stuname = "张三"

EXECUTE SQLSA USING :ll_stuid, :ls_stuname, &

:ll_stusex, :ll_stuage, :ll_stuscore;

3.9.7.4 Dynamic SQL Format3(动态 SQL 格式 3)

使用这种格式执行的语句在编译时已经知道输入的参数和结果集中的列。语法如下:

DECLARE Cursor|Procedure

DYNAMIC CURSOR|PROCEDURE FOR DynamicStagingArea ;

PREPARE DynamicStagingArea FROM SQLStatement

{USING TransactionObject} ;

OPEN DYNAMIC Cursor {USING ParameterList} ;

EXECUTE DYNAMIC Procedure {USING ParameterList} ;

FETCH Cursor|Procedure INTO HostVariableList ;

CLOSE Cursor|Procedure ;

参数

Cursor:使用的游标名。

Procedure:使用的存储过程名。

DynamicStagingArea:DynamicStagingArea 型变量名,默认为 SQLSA。

SQLStatement:包含合法的 SQL 语句的字符串,类同于前面的说明。

TransactionObject:数据库的事务对象名。

ParameterLister:用逗号分隔的一组 PowerScript 变量,前面带有冒号。

HostVariableList:存放结果的 PowerScript 变量,前面带有冒号。

注意

可以使用动态的 SELECT 语句,但 SELCT 语句中列的数目必须固定,WHERE、

ORDER BY 等子句可任意合成。也可以使用变量,但必须预先声明。

例如下列语句查询在表 STU 中满足条件的记录(可能不止一条)。

String ls_stu_name, ls_dstring

Dec ld_stu_score

Integer ld_min_age, ld_max_age

ld_min_age = 15

ld_max_age = 30

ls_dstring = "Select stu_name, stu_age from stu "+&

" where stu_age between ? and ? "

第 3 章 Pow erScript语言

— 87 —

DECLARE stu_dcur1 DYNAMIC CURSOR FOR SQLSA ;

PREPARE SQLSA FROM :ls_dstring ;

OPEN DYNAMIC stu_dcur1 USING :ld_min_age, :ld_max_age ;

FETCH stu_dcur1 INTO :ls_stu_name, :ld_stu_score;

DO WHILE SQLCA.SQLCode = 0

MessageBox("学生信息", "学生姓名:"+ ls_stu_name+&

"~n" + "成绩:" + string(ld_stu_score))

FETCH stu_dcur1 INTO :ls_stu_name, :ld_stu_score ;

LOOP

CLOSE stu_dcur1;

3.9.7.5 Dynamic SQL Format4(动态 SQL 格式 4)

也就是全动态的 SQL 语句。全动态的 SQL 语句是在编程时并不知道 SELECT 语句的

选择项是什么或有多少个选择项,也不知道 SELECT 语句中包含了多少个变量,它们是

变化的。全动态 SQL 语句支持临时生成的 SQL 语句。全动态 SQL 语句的实现很复杂,

但也很有用。语法如下:

DECLARE Cursor|Procedure DYNAMIC CURSOR|PROCEDURE

FOR DynamicStagingArea ;

PREPARE DynamicStagingArea FROM SQLStatement {USING TransactionObject} ;

DESCRIBE DynamicStagingArea INTO DynamicDescriptionArea ;

OPEN DYNAMIC Cursor|Procedure USING DESCRIPTOR DynamicDescriptionArea ;

EXECUTE DYNAMIC Cursor|Procedure

USING DESCRIPTOR DynamicDescriptionArea ;

FETCH Cursor|Procedure USING DESCRIPTOR DynamicDescriptionArea ;

CLOSE Cursor|Procedure ;

参数

Cursor:使用的游标名。

Procedure:使用的存储过程名。

DynamicStagingArea:DynamicStagingArea 型变量名,默认为 SQLSA。

SQLStatement:包含合法的 SQL 语句的字符串,类同前面的说明。

TransactionObject:数据库的事务对象名。

ParameterLister:用逗号分隔的一组 PowerScript 变量,前面带有冒号。

HostVariableList:存放结果的 PowerScript 变量,前面带有冒号。

DynamicDescriptionArea : 是 DynamicDescriptionArea 类 型 的 变 量 名 ( 默 认 为

SQLDA)。

如果 DBMS 还支持除默认 FETCH NEXT 之外的其他格式,可以定义 FETCH

FIRST、FETCH PRIOR 和 FETCH LAST 格式。

当一个语句通过 DynamicDescriptionArea 对象描述时,可通过该对象获取如表 3-18

所示的信息。

Pow erBuilder9.0 基础应用与系统开发

— 88 —

表 3-18 DynamicDescriptionArea 类型的对象的属性

参 数 信 息 NumInputs 输入参数的个数 InParmType[] 输入参数类型数组 NumOutputs 输出参数的个数 OutParmType[] 输出参数类型数组

可 通 过 函 数 获 得 某 个 输 出 参 数 的 值 和 设 置 某 个 输 入 参 数 的 值 。 函 数

DynamicDescriptionArea.SetDynamicParm(index, value)可给输入参数赋值,其中,参数

index 为 DynamicDescriptionArea 对 象 输 入 参 数 的 序 号 , 参 数 value 是

DynamicDescriptionArea 对象第 index 个输入参数要被赋的值。相应地,可用下列函数获

得输出参数的值,函数分别为:

DynamicDescriptionArea.GetDynamicDate (index) //index 表示输出参数的序号

DynamicDescriptionArea.GetDynamicDateTime ( index )

DynamicDescriptionArea.GetNumber ( index )

DynamicDescriptionArea.GetDynamicString ( index )

DynamicDescriptionArea.GetDynamicTime ( index )

输入参数和输出参数可用下列的枚举数据,以确定参数的类型。

TypeBoolean! 、 TypeDate! 、 TypeDateTime! 、 TypeDecimal! 、 TypeDouble! 、

TypeInteger!、TypeLong!、TypeReal!、TypeString!、TypeTime!、TypeUInt!、TypeULong!

和 TypeUnknown!。

1. 输入参数

可设置在语句中出现的每个输入参数的类型和值。PowerBuilder 在 DESCRIBLE 运行

时可取得 SQLDA 的属性 NumInputs。可用这个值和函数给输入参数的类型和值赋值。输

入参数是任选的,但要使用它们,应在运行 OPEN 或 EXECUTE 语句之前给它们赋值。

2. 输出参数

可以取得 PREPARE 语句中的每个输出参数的类型和值。如果使用的数据库支持输出

参数描述,PowerBuilder 应用可在 DESCRIBLE 语句执行时取得 SQLDA 的 NumOutputs

值,若数据库不支持输出参数描述,PowerBuilder 应用可在 FETCH 语句执行时取出

SQLDA 的 NumOutputs 值。可用输出参数的编号从输出参数的类型(OutParmType)数组中

获得特定的参数类型,然后再根据类型,在 FETCH 语句后,调用恰当的函数得到对应输

出参数的值。

下列示例中有一个输入参数和两个输出参数,输入参数为 TypeInteger!型,输出参数

分别为 TypeString!型和 TypeDecimal!型,例子示范了动态 SQL 格式 4 的使用。

decimal i

String ls_stu_name, ls_dstring

Dec ld_stu_score,ld_stu_sex

ls_dstring = "select stu_name, stu_score from stu "+ &

" where stu_sex = ?"

PREPARE SQLSA FROM :ls_dstring ;

第 3 章 Pow erScript语言

— 89 —

DESCRIBE SQLSA INTO SQLDA;

DECLARE stu_dcur2 DYNAMIC CURSOR FOR SQLSA;

SetDynamicParm(SQLDA, 1, 1)

OPEN DYNAMIC stu_dcur2 USING DESCRIPTOR SQLDA;

FETCH stu_dcur2 USING DESCRIPTOR SQLDA;

do while SQLCA.SQLCode = 0

for i=1 to SQLDA.NumOutputs

choose case SQLDA.OutParmType[i]

case TypeString!

ls_stu_name = GetDynamicString(SQLDA, i)

case TypeDecimal!

ld_stu_score = GetDynamicNumber(SQLDA, i)

end choose

next

MessageBox("学生信息", "学生姓名:"+ ls_stu_name+&

"~n" + "成绩:" + string(integer(ld_stu_score)))

FETCH stu_dcur2 USING DESCRIPTOR SQLDA;

loop

Close stu_dcur2 ;

3.9.8 大文本和大二进制数据的处理

大文本和大二进制数据的处理与通常的数据处理不同,有一定的特殊性。例如,

Sybase 数据库中的 text 字段或 image 字段,Oracle 数据库的 blob、long 和 long raw 字

段,这些字段最大容量都可达 2 GB,对这样大的数据处理的特殊性是可想而知的。

PowerBuilder 中提供了两个大文本和二进制数据的处理语句,一个是 SelectBlob 语

句,另一个是 UpdateBlob 语句。

1. SelectBlob 语句

SelectBlob 语句是用来从数据库表中取出大的文本字段或大的二进制字段(即大对

象)。语句如下:

SelectBlob RestOfSelectStatement {USING TransactionObject};

其中,RestOfSelectStatement 为 Select 语句中的 INTO、FROM 和 WHERE 子句。

注意该语句只能返回一条数据。TransactionObject 是数据库的事务对象名,此子句只

用于默认的 SQLCA 以外的事务对象。

下例从 personal 表中取出 pict 字段,并把它存入文件 d:\mypicture.bmp 中。

Blob picture

int fid

SelectBlob pict into :picture from personal

where id = 100001;

fid=fileopen("d:\mypicture.bmp",StreamMode!,Write!,LockWrite!,Replace!)

filewrite(fid, picture)

fileclose(fid);

if fid<>-1 then

p_1.picturename = "d:\mypicture.bmp"

Pow erBuilder9.0 基础应用与系统开发

— 90 —

end if

2. UpdateBlob 语句

UpdateBlob 语句用于修改表中的大对象的值,语法如下:

UpdateBlob TableName

SET BlobColumn = BlobVariable

RestOfUpdateStatement //Update 的后续子句

{USING TransactionObject};

其中,BlobColumn 为准备修改的大对象的列名,该列的数据类型必须为 Blob;

BlobVariable 是一个 PowerScript 类型为 blob 的变量。该语句用来修改 TableName 表的

BlobColumn 列上的数据。

下例为修改表 Personal 中的 ID 列的值为 100001 的 blob 列 pict 的程序段。

Blob picture

int fid

fid = fileopen("c:\winnt\Gone Fishing.bmp", StreamMode!)

if fid<>-1 then

fileread(fid, picture)

fileclose(fid);

UpdateBlob personal

set pict = :picture

where id = 100001;

end if

3.10 PowerScript 编程环境

前面各节较为系统地介绍了 PowerScript 的语法,而要将这些语法运用到具体的编程

中,还必须了解 PowerScript 的编程环境。本节将主要介绍 PowerBuilder 9.0 的 Script 子窗

口和 Structure 子窗口,介绍结构的创建。

在 PowerBuilder 9.0 中,PowerScript 画板被集成到了各对象画板(窗口、菜单、应用

程序对象和用户对象等)中,成为各对象画板的一个子窗口,称之为 Script 子窗口。该子

窗口具有强大功能,并且与对象画板紧密结合,所有脚本的编写包括变量的声明、函数的

定义以及用户自定义事件都可在这个编程环境中完成。

3.10.1 Script 子窗口

每个对象的子窗口都相同,均由三个下拉列表框、两个 Toggle(触发器)按钮和若干个

属性页(Event List, Fuction List 和 Declare Instance Variables 等)组成,如图 3-10 所示。三个

下拉列表框从左至右依次排列。

在第 1 个下拉列表框中,可以选择要编写脚本的对象、控件或菜单项,也可以选择

[Fuctions]和[Declare]。

第 3 章 Pow erScript语言

— 91 —

图 3-10 Script 子窗口

在第 2 个下拉列表框中,可以选择要编写的事件,函数或选择声明哪种类型的变量。

在第 3 个下拉列表框中,可以选当前对象或祖先对象,以方便地显示祖先对象的脚本

或其函数的脚本。

表 3-19 列出了这 3 个下拉列表框显示内容的对应关系。

表 3-19 3 个下拉列表框显示内容的对应关系

第 1 个列表框 第 2 个列表框 第 3 个列表框 要编写脚本的对象,控件或

菜单项 选中对象、控件或菜单项的事件或自定义事件 当前对象或祖先对象

[Functions]函数 编辑对象层函数或定义新的函数 拥有和选中函数同名的祖先对象

[Declare]声明变量和外部函数 变量的类型:全局、本地、实例或共享变量,以及全

局和本地函数 空

第 1 个 Toggle 按钮的作用是在对象层自定义事件或函数,打开原型窗口,为自定义

事件和函数定义类型、函数名或事件名、参数名、参数类型和返回值等,如图 3-11 所

示。

图 3-11 原型窗口

第 2 个 Toggle 按钮用来显示编译时的错误信息。当脚本编译出错时,该按钮自动压

下,显示错误信息。再按该按钮,将隐藏错误信息。

在编写脚本时,在各列表框中选择对应列表,在脚本输入框中编写脚本,它是一个文

本编辑器,可使用通用文本编辑器的各种编辑命令。

Pow erBuilder9.0 基础应用与系统开发

— 92 —

3.10.2 定制编程环境

打开对象后,选择 Design|Options⋯菜单项,弹出设计选项的对话框。该对话框与

PowerScript 有关的设置标签页有 General、Script(脚本)、Font(字体)、Coloring(颜色)和

AutoScript(自动编程)等 5 个,如图 3-12 所示。在这些标签页中,用户可以定制自己习惯

的编程环境。

图 3-12 设计选项的对话框和 Font 标签页

1. Font 标签页

Font 标签页主要用于设置脚本的字体类型、字体大小、下划线、前景颜色和背景颜

色,如图 3-12 所示。该标签页的使用比较简单。用户在对字体设置后,可以在该标签页

的样本(Sample)区看到字体的样式。设置完成后,可以单击 Apply(应用)按钮,在 Script 子

窗口就能看到应用效果,如果不满意当前设置,可以重新设置,当设置完成后,单击 OK

按钮保存。

2. Coloring 标签页

该标签页用于设置 Script 子窗口中各部分脚本的显示颜色,其界面如图 3-13 所示。

在 Set Color 下拉列表框中,可以对各部分脚本的显示颜色进行设置,其中各分项如

下。

(1) hite Space,空格。

(2) PowerScript Keyword,PowerScript 关键字,如 IF,WHILE 等。

(3) PowerScript Data Type,PowerScript 数据类型,如 String、DateTime 等。

(4) Integer Literal,PowerScript 中的整型数字。

(5) Float Literal,PowerScript 中的实数数字。

第 3 章 Pow erScript语言

— 93 —

图 3-13 Coloring 标签页

(6) String Literal,PowerScript 中的字符串。

(7) Date Literal,PowerScript 中的日期型数据。

(8) Time Literal,PowerScript 中的时间型数据。

(9) Symbol,PowerScript 中的运算符。

(10) Invaid Text,非法文本。

(11) Identifier,标识符。

(12) Jump Label,跳转标签。

(13) Comment,注释段。

(14) Invalid String,非法字符串。

(15) Enumeration,枚举数据类型的值,如:TypeBoolean!。

3. AutoScript 标签页

AutoScript 标签页是用来设置脚本的自动编写功能。所谓自动编写功能,是指在用户

编写脚本时,AutoScript 将提供自动查询和粘贴功能。提示要输入的属性或方法,在点操

作符后键入前一两个字符时,然后按 F8 键,去调用 AutoScript 属性,可以设置让它自动

弹出一个窗口,选择对应的函数或方法。该属性页如图 3-14 所示,页中的属性被分为 4

组。

(1) 在部分名字解析(Partial Name Resolution Include)组中,指定以下哪些项具有自动

功能:参数(Arguments)指当前函数或事件的参数,局部变量(Local Variables)指当前脚本定

义的变量,实例变量(Instance Variables)指在当前对象中定义的局部变量,共享变量

(Shared Variables)指在当前对象中定义的共享变量,全局变量(Global Variables)指在当前对

象中定义的全局变量,属性(Properties)指当前对象的属性,方法(Methods)为当前对象的事

件和函数。语句模板(Statement Templates)用于为每种语句如 IF、FOR、CHOOSE CASE、

Pow erBuilder9.0 基础应用与系统开发

— 94 —

DO 和 TRY 生成模板,内中含有注释以说明应在哪里插入代码。

图 3-14 AutoScript 标签页

(2) 在点操作符后(After a Dot Include)组里,指定属性(Properties)、方法(Methods)和

实例变量(Instance Variables)是否具有自动功能。若有,当在一个对象名后输入一个点操作

符,等几秒钟之后,会弹出一个窗口,显示当前对象可访问的属性、方法或实例变量,可

在列表中选择需要的项,然后按 Enter 键,那么该项就自动输入到脚本中了。如图 3-15 所

示。

图 3-15 自动编码

(3) 在无上下文时(When No Context Include)组里,当光标落在一行的开头或一个空行

时,是否检查函数或事件中的参数(Arguments)、本脚本的局部变量(Local Variables)、实例

变量(Instance Variables)、属性(Properties)和方法(Methods)的正确性。

第 3 章 Pow erScript语言

— 95 —

(4) 在 Options 组里,Activate Only After a Dot 复选框确定是否只有在点操作符之后才

使用自动功能,Automatic Popup 复选框确定是否自动弹出提示框。

4. Script 标签页

该标签页有一个文本框、6 个复选框和 2 个单选按钮,如图 3-16 所示。其中 Tab Size

文本框用于定义在编辑时单击 Tab 键移动的字符数。默认值为 3。

图 3-16 Script 标签页

选中 Enable Auto Indenting 时,在使用 If⋯Then 结构或循环结构时,编码将会采用自

动缩进格式。

其余 5 个复选框用于设置是否显示各种警告信息。

5. Prefixes1 和 Prefixes2 标签页

这两个标签页是用来为所有控件自动命名的。例如用户创建了一个 CommandButton

控件,PowerBuilder 自动定义其 Name 属性为 cb_1,图 3-17 所示是一些控件默认的前

缀,用户可以根据自己喜好改写这些前缀。

当开发一个大型项目时,对象命名符合一定标准对开发人员和维护人员都是同等重要

的。因此在开发前必须指定一个命名规则。PowerBuilder 的这项功能给项目开发带来很大

的帮助。

3.10.3 编程工具和编译

PowerBuilder 为用户方便地编写 PowerScript 提供了丰富的编程工具,如图 3-18 所

示。

Pow erBuilder9.0 基础应用与系统开发

— 96 —

图 3-17 控件前缀

图 3-18 PowerScript 的工具栏

各工具栏按钮的功能如下。

用于选中当前 Script 子窗口的所有脚本

用于在脚本的当前位置加注释

用于去掉脚本中当前位置注释

在脚本中查找特定内容

查找下一个内容

用指定的内容替换在文本中的特定内容

编译用户书写的脚本,用户可以随时对编写的 PowerScript 进行编译。在退出对象

画板或保存脚本时自动进行编译。也可以在编写过程中单击鼠标右键,在弹出的菜单中选

择 Compile 菜单项进行编译

用于粘贴内建的、用户自定义的和外部的函数。用此按钮编写脚本能方便查找和

避免出错

以下几个按钮的功能和粘贴函数功能类似。

粘贴 SQL 语句

粘贴 PowerScript 语句

粘贴全局函数

粘贴共享变量

粘贴实例变量

第 3 章 Pow erScript语言

— 97 —

粘贴窗口

粘贴对象

粘贴参数

3.10.4 对象浏览器

对象浏览器(Object Browser)是一个功能很强的工具。它可以让用户观察所有的对象及

其相关属性、函数、各种变量、实例、名字以及结构等与应用程序有关的信息,其界面如

图 3-19 所示。它的使用方法十分清晰:用户可先指定目标,然后按不同页面分类浏览,

此处不再赘述。

图 3-19 对象浏览器

3.10.5 函数画板的使用

前文述及,在 Script 子窗口中可以创建对象级函数,但若要创建全局函数,就需要使

用 Function Painter(函数画板)。

在 PowerBuilder 中,要创建一个全局函数,需要单击 New 按钮,在 New 对话框中选

择 PB Object 标签页,双击 Function 图标,打开函数画板,如图 3-20 所示。

图 3-20 函数画板

Pow erBuilder9.0 基础应用与系统开发

— 98 —

可以发现,该画板和函数子窗口基本一样,使用方法也相同。函数画板包括 3 个子窗

口。

(1) Script 子窗口:用于为函数书写脚本。

(2) Structure List 子窗口:用于显示全局结构列表。

(3) Structure 子窗口:用于定义全局结构变量。

要新建一个全局函数,可单击 New 按钮,双击 Fuction 图标,打开函数画板,然后完

成下列各步。

(1) 决定该函数的访问类型。

(2) 决定该函数是否有返回值(无返回值的函数又称为子例程)。

(3) 给函数命名,最好有一定的提示意义。

(4) 为函数定义参数(包括传递类型、参数类型及参数名)。

要为全局函数添加一个或删除一个参数,可以在原型窗口中单击鼠标右键,会弹出如

图 3-21 所示的菜单,然后按需要选择菜单项进行操作。

全局函数适用于在多个对象中使用,因为它本身是一个独立的对象。

若要修改该全局函数,打开它,用上述的方法来修改。

3.10.6 结构画板的使用

和函数画板一样,结构画板用于创建和修改全局结构,而对象层的结构已经被集成到

对象画板的 Structure List 和 Structure 子窗口中去了,因为在对象层,不管是函数还是结

构,都是对象的一部分,而不是独立的对象。

要创建一个全局结构,可单击工具栏的 New 按钮,在 PB Object 标签页选择 Structure

图标,双击该图标,得到结构画板,如图 3-22 所示。

图 3-21 弹出菜单 图 3-22 结构画板

在结构定义完成后,单击 Save 按钮,弹出 Save 对话框,为结构命名后,作为

PowerBuilder 独立的对象在库中保存,在对象浏览中可以查看到它。打开它,用上述的方

法来修改,还可修改该全局结构。

3.11 PowerScript 编程实例

当应用涉及到日期的相关问题时,计算每月的最后一天是一件比较繁琐的事情,因为

月份有大、小月之分,而且对于二月份其最后一天还要取决于当年是否是闰年。鉴于这种

第 3 章 Pow erScript语言

— 99 —

情况,可以变换一种思路来思考此问题:先取下一月的第 1 天,然后再通过系统提供的函

数计算下月第 1 天的前一天,即可求得当月的最后一天。

为了达到此功能,设计的主界面如图 3-23 所示。

图 3-23 主窗口界面

其中命令按钮 cb_cal 按钮的 text 为“计算”,在其 Click 事件中编写代码如下:

integer li_month

integer li_year

date ld_newdate

date ld_curlday

date ls_curdate

ls_curdate=today() //取当前日期

li_month=month(ls_curdate) //当月

li_year=year(ls_curdate) //当年

if li_month<12 then

li_month++

else

li_month=1

li_year++

end if

//下一个月的第 1 天

ld_newdate=date(li_year,li_month,1)

//当月最后一天

ld_curlday=relativedate(ld_newdate,-1)

//在单行编辑框中显示

sle_1.text=string(ld_curlday)

当程序运行时,当前月的最后一天就会显示在单行编辑框中,如图 3-24 所示。

图 3-24 程序运行界面

Pow erBuilder9.0 基础应用与系统开发

— 100 —

3.12 练习题

(1) 请找出下列程序段中的错误:

Integer Int1,INT2,Int1-INT2,Array1[100]

String myString1,myString2,2ndString

int1=0

int2=Int1-INT2

MYSTRING1=�String1� Mystring2=myString1+�and string2� 2ndString=�OK� Array1[0]=INT1+Int1 �

(2) 在 PowerBuilder 中为什么要引入 Any 数据类型?它能给编程人员带来哪些方便性?

(3) 实例变量有几种访问控制权限?它们分别是什么?

(4) 在 PowerBuilder 编程中为了方便代码的重复使用引入了哪几个代词?它们的适用

场合分别是什么?

(5) 请举例说明截获系统运行时错误 (RuntimeError)和用户自定义意外事件错误

(Exception)的方法。截获并处理错误的好处是什么?

(6) 什么是宿主变量?什么是指示器变量?为什么要引入指示器变量?

(7) 什么是存储过程?它和一般的 SQL 语句有什么区别?使用存储过程的基本步骤

是什么?

(8) 游标的工作机制是什么?它是如何来协调集合和记录之间的相互作用的?定义和

使用游标的基本步骤是什么?

(9) 为什么要引入动态 SQL?什么是动态 SQL?它的执行方式有哪几种?其使用场合

分别是什么?

(10) 对应于其他应用软件中对大文本和大二进制数据的处理,PowerBuilder 提供了哪

些相应的措施?

(11) 希望检索出 02 号部门所有员工的姓名,下列程序段能否实现这一功能?如果不

能,请对下列程序做出适当的修改,使其具有此功能(假定 SQLCA 已建立了与示例数据

库的连接):

String Emp_name,Emp_zgh1,Emp_zgh2 SELECT zgh,zgm INTO :Emp_zgh1,:Emp_name FROM zgb WHERE bmh=�02� USING SQLCA; DO WHILE SQLCA.SQLCode=0 AND SQLCA.SQLNRows>0 //处理检索出的职工信息 Emp_zgh2=Emp_zgh1 SELECT zgh,zgm INTO Emp_zgh1,:Emp_name FROM zgb WHERE bmh=�02� AND zgh<>:Emp_zgh2 USING SQLCA; LOOP

第 4 章 菜单的设计与使用

菜单是 PowerBuilder 应用中的重要组成部分,用户是借助菜单来操纵应用程序的,

对于一个目前广泛应用的 MDI(多文档界面),主窗口上必然包括菜单对象。因此,菜单设

计的好坏直接决定了应用程序的友好程度。本章将介绍菜单的基本概念和设计与使用菜单

的基本方法。此外,对于菜单的一些使用技巧也进行了详细的叙述。

4.1 设计菜单

4.1.1 菜单的基本术语

关于菜单的基本概念有:MenuItem(菜单项)、MenuText(菜单标题)、Accelerator(加速

键)、Shortcut(快捷键)、Toolbar(工具栏)等,下面仿照 Word 的风格建立一个菜单来解释这

些术语。所建菜单如图 4-1 所示。

菜单项

菜单标题

菜单栏

工具条

分隔线

省略号

快捷键

加速键

图 4-1 菜单应用

下面对这些菜单的概念进行说明。

(1) 菜单项:一个菜单对象下的指定菜单对象,一般用来实现一个指令或打开某个菜

单项引出级联的菜单。需要指出,seperationlines(分隔线)也是一个菜单项。

(2) 菜单标题:说明菜单作用的文本。如果菜单项包含快捷键(比如 F5 或 ALT+O),

则该菜单的文本中包括快捷键;如果菜单项是一个分隔线,则这个菜单项的标题就是一条

线。

(3) 加速键:通常在菜单项后的括号中用一个字母加下划线表示。当对应的菜单项在

屏幕上可见时,用户只需按加速键,就可以实现对应菜单项的功能。

(4) 快捷键:出现在菜单项的右边,当窗口获得焦点(不像加速键,仅当对应的菜单项

在屏幕上可见才有效)时,按下该快捷键,便可快速实现该菜单项功能。比如,在 Word

Pow erBuilder9.0 基础应用与系统开发

— 102 —

中用 Ctrl+O 可实现“打开”功能。

(5) 省略号:表明启动该菜单项会打开一个对话框而并不立即执行命令。如图 4-1 中

所示的“另存为�”菜单项。执行该菜单命令将打开一个对话框,以便用户指定所存文件

的路径、名称等,此时在该菜单的文本中要加上省略号。

4.1.2 菜单的类型

一般来说,菜单分为以下 3 种。 (1) DropdownMenu(下拉式菜单)。 (2) PopupMenu(弹出式菜单)。 (3) CascadingMenu(级联式菜单)。

4.1.3 Menu Printer(菜单画板)工作区

PowerBuilder 默认的菜单画板有 4 个子窗口,可以通过 View 菜单来打开或关闭某些

菜单子窗口。 为了方便菜单设计,Powerbuilder 9.0 将菜单画板工作区划分为 9 个子窗口,这比

PowerBuilder 7.0 多了一个 Variable 子窗口,PowerBuilder 提供的菜单画板如图 4-2 所示。

图中的 View 下拉菜单,显示了这 9 个子窗口的名称,可单击其中任何一个菜单项打开对

应的子窗口。

图 4-2 Menu Printer(菜单画板)

此外,View 下拉菜单还增加了一个布局(Layouts)菜单项,用户可以利用它来设置特

定的便于使用的菜单设置画板。比如,用户在进行菜单设置时只希望使用 WYSIWYG Menu View 与 Properties 子窗口,而且是某一特定布局,则用户可以把此布局保存起来,

下一次进行菜单设计时,打开此布局进行菜单设计。 图的中下部展开了 4 个子窗口,在中部,从左到右依次是 WYSIWYG(所见即所得菜

单 窗 口 ) 、 Tree Menu View( 树 状 菜 单 窗 口 ) 及 Properties( 特 性 窗 口 ) 。 在

第 4 章 菜单的设计与使用

— 103 —

WYSIWYGMenuView 或 TreeMenuView 子窗口中,可对菜单进行添加、删除、插入等操

作;在 Properties 子窗口中,可设置菜单的属性;在 Script 子窗口中,给菜单添加脚本。

4.1.4 创建菜单图

4.1.4.1 菜单属性

有关菜单的一些常见属性见表 4-1。

表 4-1 菜单属性

菜单属性 属性值 作 用 Checked Boolean(false,true) 指定那个菜单被选中,选中时其旁边有标记(√)Enabled Boolean(false,true) 指定该菜单项是否有效,如无效,则不响应任何操作

Default Boolean(false,true) 该属性为真时,菜单项显示为粗体字Name String 设置菜单项的名称,默认名称前缀为 m_Item[] Menu 指定某一菜单对象下的所有菜单对象

MenuItemType MenuItemType 标识不同平台下使用的特定菜单对象MergeOption MenuMergeOption 指明 OLE2.0 对象被激活时菜单合并后的显示方式

Microhelp String 设定菜单项的微帮助信息,该信息出现在状态栏中ParentWindow Window 指定菜单所属的窗口

Visible Boolean(false,true) 指定菜单对象是否可见,值为真时菜单可见

在对 Menu(菜单)画板以及菜单的属性有一定的了解之后,下面学习如何创建一个菜

单。一般用两种方法创建菜单,一种方法是手工创建,另外还可以通过继承祖先菜单来创

建所需要的菜单。

4.1.4.2 手工创建

手工创建菜单比较灵活,但没有继承方式快捷方便。手工创建菜单的步骤如下。

(1) 首先打开一个工作区或新建一个工作区,然后单击 PowerBuilder 菜单栏上的

File|New...,或单击工具栏上的图标 ,在打开的对话框中选择 PB Object 标签页;在 PB

Object 标 签 页 中 选 中 Menu, 然 后 单 击 OK 按 钮 ; PowerBuilder 会 弹 出

的菜单画板,如图 4-2 所示。

在菜单画板中选择某一菜单项并右击,会弹出如图 4-3 所示的用于设计菜单的快捷菜

单。菜单的功能已在图中做出了解释,此处不再赘述,开发人员可用此来进行菜单设计。

此外,还可使用 PowerBuilder 工具栏来设计菜单。

(2) 选中 WYSIWYG 子窗口的菜单项 Untitled0,在快捷菜单中选择 Insert Submenu

Item 项,插入第 1 个菜单项“文件”,并在此菜单项的 Name 属性中输入菜单项的名称。

然后再为此菜单项设置其他属性。

(3) 选中“文件”菜单项,在快捷菜单中选择 Insert Menu Item At End 项,在“文

件”菜单项后,添加菜单项“编辑”,设置菜单项的属性。

(4) 选中“文件”菜单项,在快捷菜单中选择 Insert Submenu Item 项,为“文件”菜

单项添加子菜单项“新建”,设置菜单项的属性。

(5) 添 加 其 他 菜 单项 ,其 方 法 与 设 计上 面 菜 单项 的 方 法 一 样, 保 存 菜单 名 为

m_chapter6。

Pow erBuilder9.0 基础应用与系统开发

— 104 —

菜单脚本

删除当前菜单项,并把它放入剪贴板]

拷贝当前菜单项

粘贴剪贴板中的菜单项

删除当前菜单项

复制当前菜单项 编辑当前菜单文本

插入当前菜单项同一级的菜单项

在最后插入与当前菜单项同一级的菜单项

插入当前菜单项的子菜单 菜单属性

图 4-3 用于设计菜单的弹出快捷菜单

4.1.4.3 通过菜单继承

使用继承的方式来创建菜单也是比较常用的一种创建菜单的方式。其优点在于子菜单

可以直接继承父菜单中的属性、内容以及事件脚本等。根据子菜单需要还可以很方便地进

行调整,既快捷又灵活。

通过继承创建菜单的方法如下。

(1) 单击 PowerBuilder 菜单栏上的 File|Inherit..,或单击工具栏上的图标 ,在打开

的 Inherit from Object 对话框中选择要继承的父菜单所在的 Target(目标),以及此目标下的

库,然后选择菜单,使对话框下方的 ObjectType 下拉框的选项为 Menu,单击 OK 按钮,

PowerBuilder 会弹出 的菜单画板。其中 m_chapter6 是所

指定父菜单的名称。

(2) 根据需要对继承下来的菜单进行修改,然后为菜单对象命名并保存。

4.1.4.4 设计菜单的一些技巧

(1) 要创建一个分隔行,直接输入一个破折号(——)作为菜单项的文本(Text)。

(2) 属性 General 标签中的 Name 属性为菜单项的标识,当 LockName(决定是否能修

改菜单的名称)选项被选中,而插入的菜单项为分隔行,注意要将 LockName 选项去掉,

并为此菜单项命名(如 m_10)。

(3) 要为菜单项添加加速键,直接在要定义为加速键的字母前加“&”,如菜单项

“&Paste”中 P 为加速键,菜单项“新建(&N)...”中 N 为加速键。

(4) 要定义快捷键,先在属性页中的 ShortcutKey 下拉列表中选一字符,然后选中该

下拉列表框下方的复选框 ShortcutAlt、ShortcutCtrl、ShortcutShift 其中任何一个,快捷键

就由选中的字母与对应选中的 Alt、Ctrl、Shift 组合而成。

(5) 如果要修改菜单的位置,可以直接将菜单项拖动到想要调整的位置。但对继承下

来的菜单,要想把添加在菜单末尾的菜单项往上移动,即相当于在原父菜单中间位置插入

一个新的菜单项,则必须对父菜单项的 ShiftToRight 进行设置。如果未设置,后代菜单只

能在菜单的末尾插入菜单项。

第 4 章 菜单的设计与使用

— 105 —

4.2 设计工具栏

为了让应用程序使用更加方便,应该在应用程序中增加工具栏。工具栏上的图标为菜

单项的选择提供了一种快捷方式,只需单击工具栏上的图标就可实现相应菜单项的相同功

能。本节主要介绍如何为菜单加入工具栏、设置工具栏的属性以及在程序运行中随时调整

工具栏的属性(比如改变工具栏的位置、标题以及是否可视等属性)等。

4.2.1 为菜单添加工具栏

在创建好菜单之后,如果要想将某些菜单项显示在工具栏上,就应在菜单画板中定义

工具栏,并设计相应工具栏按钮的显示文本、提示框等特征。

分隔线或拥有下一级子菜单的菜单项不应该在工具栏上添加图标,因为这没有任何意

义。添加了工具栏之后,PowerBuilder 自动为应用程序(必须是 MDI 框架或 MDI 的 Sheet)

生成工具栏,并为所生成的工具栏提供一个名为 FrameBar 的弹出菜单。

一个菜单通常有几组不同的功能,因此菜单拥有多个工具栏便显得非常有必要。事实

上,当某个 MDI 框架或 Sheet 菜单连接了多个工具栏时,运行中将显示与之相关的所有

工具栏。这些工具栏都是独立的,每个工具栏都具有自己的弹出菜单,可以分别设置这些

工具栏的显示方式。

为菜单设计工具栏是在菜单的属性区中的 Toolbar 页中进行的,其属性及说明如图 4-

4 所示。

当鼠标置于该图标按钮上时的提示文本。如左,当鼠标放在 按钮上时,该图标

按钮下方将会出现一个“新建”的黄色提示栏

给当前菜单添加工具栏图标按钮。可以单击右侧 按钮,从下拉列表框中选择一

储备图片,也可浏览选择一 BMP 图片

设置当鼠标按下时显示的图标。如左,当鼠标按下时显示 ,而鼠标释放时则显

示为

选定时工具栏图标按钮为可见

选定该项让工具栏图标按钮在初始化时显示按下时的图标

指定该工具栏按钮图片之前的空格数目。0 表示不留任何空白位置

定义图标按钮显示在工具栏上的位置。如果为 0,表示按菜单项的定义顺序进行排

当存在多级工具栏时,指定菜单对象在哪一个工具栏上。属性相同的图标按钮将

显示在一个工具栏上,如果定义了 n 个工具条,属性值范围为 1 到 n

指定图标按钮对象类型,有“Menu”和“MenuCascade”两种类型。当选择

“MenuCascade”时,表示定义下拉式工具栏,PB 会自动弹出“Dropdown”和“Column”

属性(下拉工具栏的列数),开发者可根据需要进行设置

图 4-4 Toolbar 属性页

Pow erBuilder9.0 基础应用与系统开发

— 106 —

设置 Toolbar 中的属性 ToolbarItemIndex 便可实现多个工具栏,如工具栏 1 的

ToolbarItemIndex 为 1,工具栏 2 的 ToolbarItemIndex 为 2,依此类推。

为菜单添加工具栏的步骤如下。

在打开的 Menu Painter 中,选中一菜单对象(注意不要是分隔线或是包括子菜单的菜

单项),然后在 Properties 区选中 Toolbar 页。

在 Toolbar 页中的 ToolbarItemName 下拉框中为工具栏选择图标;也可单击右边的图

标 ,选择想要的其他图标。

选择完图标后,还可设置其他属性,这些属性见图 4-4。如果不需要其他功能,为菜

单添加工具栏的工作便完成了。接着为其他菜单项做类似的工作,指定其他菜单项对应的

图标。

4.2.2 在程序中管理工具栏

下面以一个小例子来说明怎样在程序中管理工具栏。图 4-5 是例子运行时的外观。通

过本例,向大家介绍以何种方式来显示工具栏,如何管理多级工具栏,如何防止用户移动

工具栏等内容。

图 4-5 在程序中管理工具栏的示例

实现该例子的步骤如下。

(1) 建立一 个 MDI 的 框架窗 口 w_frame , 将此窗口 的属 性 WindowType 设 为

mdihelp!,然后将在本章 4.1.4 节建立的菜单 m_chapter6 挂到此窗口上(如何把菜单挂到窗

口上请参照本章的 4.3.1 节)。其中 m_chapter6 上有一菜单项“格式”,其子菜单项有“工

具栏”,其功能为打开“工具栏管理”(w_toolbar)窗口。

(2) 建立一个 MDISheet 窗口 w_toolbar,而将窗口 w_toolbar 的属性 WindowType 设

为 main!。

下面,分别介绍如何实现程序中的各项功能。

为 了 实 现 以 下 各 项 功 能 , 首 先 定 义 两 个 实 例 变 量 ( 在 窗 口 的 declare 中 定 义

InstanceVariables):

第 4 章 菜单的设计与使用

— 107 —

application iapp_application //当前应用

window iw_window //窗口变量

在 w_toolbar 窗口 Open 的事件中,写以下代码:

iw_window=this.parentwindow() //得到当前拥有工具栏的窗口句柄

iapp_application=GetApplication() //得到当前应用的句柄

变量定义之后,再依次完成以下功能。

1. 实现多级工具栏的显示与隐藏

为了决定要显示或隐藏工具栏,以及要对哪一个工具栏进行显示与隐藏工作,程序中

使用了两个多选按钮(Checkbox):cbx_1 与 cbx_2,使用 cbx_1,决定是否要隐藏工具栏

1;使用 cbx_2,决定是否要隐藏工具栏 2,其代码如下。

(1) 在控件 cbx_1 的 Clicked 事件中,写入如下代码:

if cbx_1.checked then //如果要隐藏工具栏 1

iw_window.toolbarvisible=false //则设置工具栏 1 的“可见”属性为“假”

else //否则

iw_window.toolbarvisible=true //设置工具栏 1 的“可见”属性为“真”

endif

(2) 在控件 cbx_2 的 Clicked 事件中,写入如下代码:

if cbx_2.checked then //如果要隐藏工具栏 2

iw_window.settoolbar(2,false) //用 settoolbar 函数来实现工具栏 2 的

“可见”属性为“假”

else //否则

iw_window.settoolbar(2,true) //将“可见”属性设为“真”

end if

2. 获取多级工具栏的位置

首先,在 w_frame(MDI 窗口)定义 4 个全局变量,用以存放工具栏 1、二的位置参

数,这 4 个变量为 integer dockrow1,dockrow2,offset1,offset2。

(1) 在 w_frame 的 toolbarmoved(当工具栏移动时)写以下代码:

integer li_dockrow1,li_offset1,li_dockrow2,li_offset2

w_frame.GetToolbarPos(1,li_dockrow1,li_offset1)//取得工具栏 1 的位置

w_frame.GetToolbarPos(2,li_dockrow2,li_offset2)//取得工具栏 2 的位置

dockrow1=li_dockrow1//将工具栏 1 所在的行数赋给 dockrow1

offset1=li_offset1//将工具栏 1 的偏移量(x 的值) 赋给 offset1

dockrow2=li_dockrow2//将工具栏 2 所在的行数赋给 dockrow2

offset2=li_offset2//将工具栏 2 的偏移量(x 的值) 赋给 offset2

(2) 在 w_toolbar 窗口设置一个定时器,在 w_toolbar 的 Open 事件中,写入:

timer(0.1) //每 0.1 秒触发一次事件

(3) 在 w_toolbar 窗口的 timer 事件中写入代码(其中,mle_1 与 mle_2 为程序中的两个

Pow erBuilder9.0 基础应用与系统开发

— 108 —

单行编辑框):

//存放工具栏 1 的位置,dockrow1 为所在行,offset1 为偏移量

mle_1.Text=" 第 "+string(dockrow1)+" 行 "+"~r~n"+" 偏 移 量 为 :

"+string(offset1)

//存放工具栏 2 的位置,dockrow2 为所在行,offset2 为偏移量

mle_2.Text=" 第 "+string(dockrow2)+" 行 "+"~r~n"+" 偏 移 量 为 :

"+string(offset2)

3. 实现多级工具栏的移动

为 了 控 制 工 具 栏 1 和 工 具 栏 2 的 移 动 , 程 序 中 用 两 个 下 拉 列 表 框

(DropDownListBox),控制工具栏 1 用的是 ddlb_toolbar1 控件,其中有 5 个下拉项为:

“左”、“上”、“右”、“下”、“浮动”,分别控制工具栏 1 的停泊位置。控制工具栏 2 用的

是 ddlb_toolbar2 控 件 , 其 中 也 有 跟 ddlb_toolbar1 一 样 的 5 个 下 拉 项 , 作 用 跟

ddlb_toolbar1 一样。

(1) 在控制工具栏 1 的控件 ddlb_toolbar1 的 selectionchanged 事件中写入:

choose case ddlb_toolbar1.text

case "左"

iw_window.toolbaralignment=AlignAtLeft! //将工具栏 1 置于窗口的左边

case "上"

iw_window.toolbaralignment=AlignAtTop! //将工具栏 1 置于窗口的上方

case "右"

iw_window.toolbaralignment=AlignAtRight! //将工具栏 1 置于窗口的右边

case "下"

iw_window.toolbaralignment=AlignAtBottom!//将工具栏 1 置于窗口的底部

case "浮动"

iw_window.toolbaralignment=Floating! //使工具栏 1 能在窗口中浮动

end choose

iw_window.toolbaralignment=AlignAtLeft!//将工具栏 1 置于窗口的左边

(2) 在控制工具栏 2 的控件 ddlb_toolbar2 的 selectionchanged 事件中写入:

choose case ddlb_toolbar2.text

case "左" //用 settoolbar 函数将工具栏 2 置于窗口的左边,其中第 2 个参数表示工具

栏是否可见

iw_window.SetToolbar(2, TRUE, AlignAtLeft!)

case "上" //将工具栏 2 置于窗口的上方

iw_window.SetToolbar(2, TRUE, AlignAtTop!)

case "右" //将工具栏 2 置于窗口的右边

iw_window.SetToolbar(2, TRUE, AlignAtRight!)

case "下" //将工具栏 2 置于窗口的底部

iw_window.SetToolbar(2, TRUE, AlignAtBottom!)

case "浮动" //使工具栏 2 能在窗口中浮动

iw_window.SetToolbar(2, TRUE, Floating!)

end choose

第 4 章 菜单的设计与使用

— 109 —

4. 锁定多级工具栏(禁止工具栏的移动)

用 CommandButton( 命 令 按 钮 )cb_locktoolbar 来 控 制 是 否 锁 定 工 具 栏 , 在

cb_locktoolbar 的 Clicked 事件中写入代码:

if iapp_application.toolbarusercontrol=true then //如果原来没有锁定工具

栏,现在单击“锁定”按钮

cb_locktoolbar.text="解锁" //按钮的文本变为“解锁”(初始文本为“锁定”)

iapp_application.toolbarusercontrol=false//锁定工具栏

else //如果原来锁定工具栏,现在单击“解锁”按钮

iapp_application.toolbarusercontrol=true //解锁工具栏(可以移动工具栏)

cb_locktoolbar.text="锁定" //使按钮的文本变为“锁定”

end if

5. 其他功能

(1) 设置工具栏以文本方式显示提示信息用下述代码:

iapp_application.toolbartext=true

(2) 设置工具栏以提示框方式显示提示信息用下述代码:

iapp_application.toolbartips=true

4.3 菜单的使用与管理

当用户完成了设置窗口、设置菜单、为菜单添加工具栏等工作以后,接着便是怎样把

窗口与菜单联系起来、是否需要在运行时改变菜单、怎样根据菜单项完成相应的功能以及

如何通过菜单使系统变得更安全。

4.3.1 把菜单挂到窗口上

当建好窗口,设置好菜单之后,需要把菜单与窗口进行连接,以通过菜单项对窗口进

行操作。下面介绍如何进行菜单的引用。

有两种方法实现窗口的引用,其中一种方法为在设置窗口时用属性设置来实现窗口与

菜单的关联;另外一种是在程序运行时改变菜单。

4.3.1.1 在设置窗口时实现窗口与菜单的关联

首先,打开一个想引用菜单的窗口。然后,在窗口属性区的 General 的 MenuName 属

性中直接填入想引用的菜单的名称,这就把窗口与菜单联系起来了,其做法如图 4-6 所

示。

也可以通过单击 Browse(浏览)按钮选择菜单,打开一个选择对象的对话框。在对

话框中选择想要引用的菜单(确保选择合适的库 Application Libraries),单击 OK 按钮。

Pow erBuilder9.0 基础应用与系统开发

— 110 —

直接输入想

要连接的菜

单的名称 通过浏览选择菜单

图 4-6 菜单引用窗口

4.3.1.2 在程序中改变菜单

在程序中,如果根据不同用户的不同情况,提供不同的菜单。比如对熟练用户提供一

些附加的功能,而对新手则不需要。这个功能是不能通过设值属性实现的,只能在程序中

用代码实现。下面通过一个小例子来说明在程序中实现改变菜单的操作,这个例子运行时

的外观如图 4-7 所示。

图 4-7 在程序中改变菜单示例

在窗口上显示的菜单只是窗口的一个属性,与几乎所有的属性一样,在运行时可以改

变与窗口相联系的菜单。但是,PowerBuilder 不允许用户通过标准的“.”记号来完成这

一工作,因为菜单被认为是一类 Protect(受保护)的属性。受保护的属性只能通过作为对象

界面的函数来访问,在这个例子中,调用了窗口对象的函数 ChangeMenu()实现在程序中

改变窗口的菜单。

建立程序的步骤如下。

(1) 首先建立一个“简单模式”菜单,实现一些基本功能,然后,在这个菜单的基础

上继承建立另外一个“复杂模式”菜单。

(2) 在“简单模式”菜单中有一菜单项“复杂模式”,其 Clicked 事件的代码为:

//将菜单模式变为“复杂模式”,parentwindow 为拥有 m_chapter6_inherit 的窗口

parentwindow.changemenu(m_chapter6_inherit)

(3) 在“复杂模式”菜单中有一菜单项“简单模式”,其 Clicked 事件的代码为:

第 4 章 菜单的设计与使用

— 111 —

//将菜单模式变为“简单模式”,parentwindow 为拥有 m_chapter6 的窗口

parentwindow.changemenu(m_chapter6)

4.3.2 为菜单项添加脚本

为了通过菜单完成某项操作,必须在菜单事件中写入适当的代码来加以实现。为了更

好地操纵菜单,必须熟悉菜单的事件与函数。

4.3.2.1 菜单事件

菜单的事件很少,只有 Clicked,Help,Selected 共 3 个事件。

1. Clicked 事件

Clicked 事件是菜单中最常用的事件,在通常的情况下,每个下拉式菜单或级联式菜

单项都有一个 Clicked 事件的代码。当用户单击菜单项、使用键盘选中某一菜单项(高亮显

示)并按 Enter 键、按下菜单项的快捷键时此事件触发。

例如,在教学管理的“教学计划制订”的菜单中打开教学计划窗口,clicked 事件中

的代码为:

opensheet(w_jxjh,w_frame,2,original!)

该函数在 MDI 窗口 w_frame(主窗口)中以原始尺寸打开子窗口(工作表)w_jxjh。此时

窗口上的菜单是与子窗口 w_jxjh 相连的菜单,而不是 w_frame 的菜单。

又如,在每个菜单中,一般都有实现退出程序的菜单项,可在菜单项的 Clicked 事件

编写代码:

close(parentwindow)

这里 parentwindow 是引用菜单的父窗口,使用 close 关闭拥有菜单的父窗口,即可退

出该菜单项。

2. Help 事件

当用户按 F1 或窗口标题上的上下文帮助按钮,且选中并单击菜单项时激活 Help 事

件。Help 常用于在特定位置显示帮助信息。如在一菜单项的 Help 事件中有下述代码:

MessagBox(�上下文帮助信息�,�您正在查看帮助信息�)

当用户选中了这个菜单项,按下 F1,就会出来一个消息框。

3. Selected 事件

当用户用键盘或鼠标将焦点移到某一菜单项(不需要执行)时,激活此事件,Selected

事件较少用来实现具体操作。Selected 事件一般用来显示菜单项的帮助信息,保存菜单项

帮助信息的一种方法是利用菜单项的标志信息(tag)。如在一菜单项的 Selected 事件中写入

如下代码:

this.microhelp=this.tag

Pow erBuilder9.0 基础应用与系统开发

— 112 —

若此菜单项的 tag 属性为“编辑”,Microhelp 属性为空,当选中了此菜单项时,其帮

助信息为“编辑”。

4.3.2.2 菜单常用函数

1. Show()与 Hide()函数

这两个函数控制菜单对象的可见与否,其作用相当于属性中的 visible。例如,有下面

的语句:

if m_chapter6.m_insert.visible=true then//如果此菜单项属性为“可见”

m_chapter6.m_insert.hide() //则让此菜单项“隐藏”

else //如果此菜单项“不可见”,则显示此菜单项

m_chapter6.m_insert.show()

endif

若执行此程序,则可交替控制菜单项 m_chapter6.m_insert 的可见属性。

2. Disable()与 Enable()函数

控制菜单项的可用与否,其作用与菜单属性中的 Enable 的作用是相同的。

3. Check()与 Uncheck()函数

该函数用于标记一菜单项,指定此菜单项是否加上标记“√”,和使用属性中的

Checked 的效果一样。

4.3.2.3 菜单的控制

在以往的应用程序中,为了赋予用户不同的权限,通常使用数据库,以数据表的形式

存储用户信息。由于涉及读取库的操作,通常速度较慢。下面,介绍如何通过菜单实现类

似功能。

用一个 Treeview 控件显示菜单项,然后直接通过 Treeview 控制菜单项的可用性,其

程序运行时的界面如图 4-8 所示。

图 4-8 菜单控制程序运行示例

第 4 章 菜单的设计与使用

— 113 —

主要设计内容如下:

(1) 建立窗口 w_menutree,在窗口的 Open 事件中写入下列代码,实现从菜单中取出

其所有菜单项,然后在 Treeview 控件中建立一个菜单树。

clicknum=0 //菜单双击的次数(为实例变量),控制菜单的可用与否

iw_window=this.parentwindow() //得到当前窗口父窗口的句柄(为实例变量)

string ls_level1,ls_level2

long ll_handle

integer ii,jj,level1item,level2item

level1item=upperbound(iw_window.menuid.item) //取得一级菜单的数量

for ii=1 to level1item //遍历一级菜单

ls_level1=iw_window.menuid.item[ii].text //取得菜单的文本

if ls_level1<>"-" then //如果此菜单不为分隔线

//将一级菜单项插入到 Treeview 控件的第 1 级的相应项,其中 ll_handle 为要插入第 2 级

菜单的上一级菜单的句柄,Pictureindex=1

ll_handle=tv_1.insertitemlast(0,ls_level1,1)

end if

//取得某一一级菜单的对应二级菜单的数量

level2item=upperbound(iw_window.menuid.item[ii].item)

for jj=1 to level2item //遍历某二级菜单

ls_level2=iw_window.menuid.item[ii].item[jj].text //取得菜单的文

if ls_level2<>"-" then //如果此菜单不为分隔线

//将菜单文本插到相应一级菜单的下一级菜单中,Pictureindex=2

tv_1.insertitemlast(ll_handle,ls_level2,2)

end if

next

next

(2) 在 Treeview 中,双击某一二级菜单项,当此菜单项为可用时,将其变为不可用,

反之,则将其变为可用。

· 在 Treeview 的项目展开事件 itempopulate 中输入以下代码以便取得展开项的文

本。

treeviewitem l_tvi

this.getitem(handle,l_tvi) //取得当前展开项的句柄

parentitemtext=l_tvi.label //取得当前展开项的文本

· 在 Treeview 的 doubleclicked 事件中写入以下代码,实现对菜单可用性的交替控

制。

integer enableordis //控制菜单项的可用与否的变量的定义

clicknum=clicknum+1 //双击一次,数量加 1

enableordis=mod(clicknum,2) //将双击次数除 2 取余

TreeViewItem ltvi_current

This.GetItem(handle, ltvi_current) //取得当前双击菜单项的句柄

if ltvi_current.level=2 then //如果当前菜单项为二级

integer level1item,level1itemnum,level2item,level2itemnum

level1itemnum=UpperBound(iw_window.menuid.item)//取得一级菜单的数量

Pow erBuilder9.0 基础应用与系统开发

— 114 —

for level1item=1 to level1itemnum //遍历一级菜单

//根据所双击的 Treeview 项的上一级 item 文本寻找相应的菜单项

if iw_window.menuid.item[level1item].text=parentitemtext then //如果匹

level2itemnum=UpperBound(iw_window.Menuid.Item[level1item].Item) //

取得当前菜单的子菜单的数量

for level2item=1 to level2itemnum //遍历当前菜单的子菜单

//根据当前的 Treeview 项 item 文本寻找相应的子菜单项

if

w_window.MenuId.Item[level1item].Item[level2item].Text=ltvi_current.Lab

el then //如果匹配

if enableordis=1 then //如果当前项为可用

Disable(iw_window.Menuid.item[level1item].Item[level2item]) //则将其变为

不可用

ltvi_current.pictureindex=3 //改变当前 Treeview 项的图片

This.SetItem(handle, ltvi_current)

else //否则,将当前项置为可用

enable(iw_window.menuid.item[level1item].Item[level2item])

ltvi_current.pictureindex=2 //改变图片,将其置为原来的图片

This.SetItem(handle, ltvi_current)

end if

level2item=level2itemnum //找到了菜单项,停止遍历

Exit

end if

next

Exit

End if

Next

End if

注意

本例子中的菜单级数为 2,如果菜单级数超过 2,则需增加代码。

4.3.2.4 实现弹出菜单

在相应的窗口或控件上右击,就会在鼠标所指位置弹出菜单,这就是右键菜单。程序

中支持右键菜单会为用户的操作带来许多方便,同时鼠标右键可以分担部分左键的功能。

在 PowerBuilder 中实现右键菜单分两种情况,一种情况是菜单已经跟窗口连接起来;另

一种情况是菜单没有跟当前任何打开的窗口相联系。

对于第 1 种情况,在窗口的 MenuName 的属性中已经填入想要在窗口的某一对象中

弹出的右键菜单的名称,如 m_chapter6,只需在该对象的 rightclicked 事件的脚本中写入

如下代码,便可在单击鼠标右键时弹出此菜单。

m_chapter6.popmenu(pointerx(),pointery())

对于第 2 种情况,必须首先用 Create 方法为菜单分配内存,声明一个该菜单对象变

第 4 章 菜单的设计与使用

— 115 —

量并实例化。下面的代码可实现与前相同的功能。

m_chapter6 popmenu

popmenu=create m_chapter6

m_chapter6.popmenu(pointerx(),pointery())

注意

Popmenu 函 数 的 调 用 格 式 为 : menuname.PopMenu(xlocation, ylocation) 。 其 中 ,

menuname 可以是一菜单对象的所有菜单项,也可以是菜单对象中的某一部分。例如用户

只想打开菜单 m_chapter6 中的“m_文件”项,便可写入如下代码:

m_chapter6 popmenu

popmenu=create m_chapter6

m_chapter6.m_文件.popmenu(pointerx(),pointery())

xlocation,ylocation 指定弹出菜单在窗口上的显示位置,单位为 PowerBuilder units,它

的位置是相对于当前活动窗口的。下面的代码表示在一个 MDI 应用中,弹出菜单显示在

MDI 框架 w_frame_1 的 pointerx(),pointery()鼠标位置上:

m_chapter6.m_文件.popmenu(w_frame_1.pointerx(),w_frame_1.pointery())

4.4 菜单设计实例

菜单可以用两种方式挂接到窗口上,在此以直接在窗口的属性里进行设置来对窗口和

菜单进行关联。首先分别设计窗口对象 w_menu 和一个菜单对象 m_tmenu,菜单对象包括

3 个菜单项,分别为 m_file,m_edit 和 m_view。菜单设计界面如图 4-9 所示。

图 4-9 菜单设计界面

要将窗口 w_menu 和菜单 m_tmenu 进行关联,需要在窗口 w_menu 的属性框中进行

设置,其设置界面如图 4-10 所示。

Pow erBuilder9.0 基础应用与系统开发

— 116 —

图 4-10 设置窗口和菜单关联

要实现的功能是:单击“文件”菜单下的 “打开” 子菜单时自动打开窗口对象

w_url,如图 4-11 所示。

图 4-11 程序运行初始界面

为了实现该功能,在“打开”子菜单项的 clicked 事件中编写代码 open(w_url)。运行

程序,选择“打开“菜单,其界面如图 4-12 所示。

图 4-12 执行“打开”菜单后

4.5 练习题

(1) 要建立窗口与菜单之间的联系有几种方式?分别是什么?

(2) 带图片的菜单有两种,一种是完全的图标菜单(即只包含图标的菜单),一种是图

标与文字共存的菜单。试通过调用 Windows API 函数把指定的图标装载到指定的菜单

上。

(3) 试设计一个包含弹出菜单的窗口,使通过此菜单可以完成对窗口的所有操作。

第 5 章 数 据 窗 口

PowerBuilder 的数据窗口技术是其最具风格的特征之一。它为开发 PowerBuilder 应用

提供了自动、直观、省时和灵活的用户/数据库接口。因此,掌握并熟悉数据窗口的使用

对于 PowerBuilder 应用程序的用户来说至关重要。数据窗口可从以下两个角度来理解:

一是 Datawindow object(数据窗口对象),二是 Datawindow control(数据窗口控件)。数据窗

口对象用来从数据库取得数据,并在可供选择的多种表现形式中操纵数据;而数据窗口控

件是窗口中的一个普通控件,它可以在窗口中将相关联的数据窗口对象显示出来。二者的

结合使用使 PowerBuilder 的应用程序对数据库有灵活丰富的操纵方式。数据库、数据窗

口对象与数据窗口控件的相互关系如图 5-1 所示。

数据窗口对象

数据窗口对象

数据窗口对象

窗 口

数据窗口

控 件

图 5-1 数据窗口对象及控件间的关系

5.1 创建数据窗口对象

使用数据窗口有两个主要步骤。

(1) 用数据窗口画板建立一个新的数据窗口对象或编辑一个旧的数据窗口对象,在画

板中选择显示格式、数据源及其他属性。

(2) 在窗口画板中,把一个数据窗口控件插入窗口,并把一个数据窗口对象与之关

联。通过这个控件,应用程序窗口界面与数据窗口对象进行通信,并完成对数据的修改、

更新等操作。

数据窗口对象是一个从关系数据库或其他数据源中检索、表现和操纵数据的对象。数

据窗口对象封装了数据源的信息,也包括了有关在数据窗口对象中显示数据的格式信息。

因此,创建数据窗口对象主要涉及两个方面:一是数据窗口对象以何种方式来显示数据;

二是数据窗口对象的数据来自何处。当然,在建立新数据窗口对象之前,必须保证已经与

要访问的数据库连接无误。

5.1.1 选择数据窗口对象的显示风格

选择工具栏上的 NEW 按钮之后,再单击 Datawindow 选项,出现如图 5-2 所示界

面,以选择待建数据窗口对象的显示格式,它决定了数据窗口对象中数据的表现方式。用

Pow erBuilder9.0 基础应用与系统开发

— 118 —

户可以使用每种格式的默认布局,也可以对其进行修改以满足不同要求。PowerBuilder 共

提供了 11 种显示格式。

图 5-2 数据窗口对象显示风格

5.1.1.1 Grid 风格

Grid 风格的数据窗口对象的主要特点是:数据的行与列之间都用网格线来分隔,数

据的每一项都放在网格之中,显示比较直观、整齐。此类型的数据窗口对象常用于查询或

打印各种报表。图 5-3 所示的就是 Grid 风格的数据窗口对象的效果。

图 5-3 Grid 风格的数据窗口对象

5.1.1.2 Freeform 风格

Freeform 风格在每一屏上只能看到一条信息,其特点是布局灵活,用户可以随意移动

列和列标题,按自己的需求布局。这种风格一般特别适合作为数据录入的界面,如图 5-4

所示。

第 5 章 数据窗口

— 119 —

图 5-4 Freeform 风格的数据窗口对象

5.1.1.3 Tabular 风格

Tabular 显示风格与 Grid 风格非常相似,惟一不同的是 Tabular 风格的数据窗口对象

不用网格来分开数据行和数据列。它虽然看起来不美观,但可以为每一数据项定义边框或

底色等属性。Tabular 风格允许数据列跨列或跨带来显示。列的显示次序可以在设计时改

变,也可以在运行时改变,并且允许对数据进行分组显示,如图 5-5 所示。

图 5-5 Tabular 风格的数据窗口对象

5.1.1.4 N-UP 风格

N-UP 风格与 Tabular 的风格相似,不同之处是 N-UP 风格的数据窗口一行可以显示

多条记录。这样,当数据的列项比较少时,一屏便可以看更多的记录,以免滚动浏览。在

使用 N-UP 风格的数据窗口对象时,会出现 Specify Rows in detail 的对话框,要求指定

一行要显示的记录个数,默认为两个,如图 5-6 所示。

图 5-6 N-UP 风格的数据窗口对象

Pow erBuilder9.0 基础应用与系统开发

— 120 —

5.1.1.5 Label 风格

Label 风格的数据窗口对象是由标签组成的,它只显示数据内容,而不显示数据列的

标题,如图 5-7 所示。

图 5-7 Label 风格的数据窗口对象

建立此风格的数据窗口时,必须定义标签的参数。如果要修改 Label 风格的数据窗口

对象,可在数据窗口对象画板中选取属性图标,在出现的属性对话框中进行相应标签属性

及页面设置。在先出现的 Select Predefined Label 对话框中可选择一些预定义的标签类

型。图 5-8 给出标签属性设置界面。其中,属性对话框中的 Width 和 Heigh 用于设定标签

的宽和高,Across 设置一行中标签的个数,Down 设置一列中标签的个数,Margins

betweens 栏用于设置各个标签行和列的距离,Arrange 栏用于设置标签的布局是从左到

右,还是从上到下。

图 5-8 Label 风格的数据窗口对象属性设置界面

图 5-9 给出标签页设置界面,其中,Margin 栏用来设置标签页面在数据窗口中的位

置,其中 Top、Bottom、Left 及 Right 用来设定标签距数据窗口的各边距离。Paper 栏用于

选择纸的类型是连续方式还是单页送纸。

第 5 章 数据窗口

— 121 —

图 5-9 Label 风格的数据窗口对象属性界面

5.1.1.6 Crosstab 风格

Crosstab(交叉表)风格的数据窗口对象类似于 Grid 风格,与 Excel 电子表格的功能有

些相似,主要用于统计和分析数据,可以将检索的数据分组、分类,进行累计求和等运

算。Crosstab 数据窗口可让用户观察到多行、多列的总计数据,如图 5-10 所示。

图 5-10 Crosstab 风格的数据窗口对象

在建立 Crosstab 风格的数据窗口对象时,会出现如图 5-11 所示的 Crosstab 的定义对

话框。通过此对话框来设计交叉表,把要在交叉表中当做列来显示的数据拖动到 Columns

列表框中,把要在交叉表中当做行来显示的数据拖动到 Rows 框中,把要进行计算的数据

拖动到 Value 列表框中,系统会自动为 Value 中的列设置默认公式,此公式可以修改。图

5-10 就是将 Zydm 列拖动到 Rows 框中、把 Xxxs 列拖动到 Columns 框中、把 cj1 列拖动

到 Value 框中的显示结果。

5.1.1.7 Group 风格

Group 风格为数据窗口对象提供了一种对数据进行分组显示的简便方法。每一组都可

以带有统计数据,它实际上是一种有分组特性的 Tabular 表现风格,如图 5-12 所示。

Pow erBuilder9.0 基础应用与系统开发

— 122 —

图 5-11 定义 Crosstab

图 5-12 Group 风格数据窗口对象

在建立 Group 风格的数据窗口对象时,会出现定义 Group 的对话框,让用户输入有

关分组的信息,如图 5-13 所示。用户要按哪一列分组,就把此列从 Source Data 框中拖动

到 Columns 框中。

图 5-13 定义 Group

第 5 章 数据窗口

— 123 —

5.1.1.8 Composite 风格

Composite 风格的数据窗口对象可以将不同风格的数据窗口对象放置在一起,创建该

风格的数据窗口对象时,不必指定数据源,只要在 Choose Nested Data Windows for

Composite 对话框中把已经创建好的数据窗口对象选中就可以了。其显示效果如图 5-14 所

示,它包含了一个 Grid 风格的数据窗口对象和一个 Gragh 风格的数据窗口对象。

图 5-14 Composite 风格数据窗口对象

5.1.1.9 OLE 风格

OLE 风格的数据窗口对象允许用户在数据窗口嵌入 OLE 对象,使用被连接的对象来

处理数据库中的数据,如用 Microsoft Gragh 2000 生成图表等,这样极大地扩充了数据窗

口对象的处理能力和处理方式。在选择了数据源后,会弹出如图 5-15 所示的对话框指定

OLE 数据,其中,要将分组依据的数据列拖动到 Group by 栏,把要显示的数据拖动到

Target Data 栏。在选择了 OLE 对象后,将进入数据窗口画板,进行相应设置。

图 5-15 指定 OLE 数据

图 5-16 所显示的就是一个 OLE 风格的数据窗口对象,选择了 Microsoft Gragh 2000

Pow erBuilder9.0 基础应用与系统开发

— 124 —

图表作为 OLE 对象。

图 5-16 一个 OLE 风格的数据窗口对象

5.1.1.10 RichText 风格

RichText 风格的数据窗口对象允许用户将数据列放在文档中,可以在 PowerBuilder 之

外用文字处理软件以交互方式加工这些数据。换言之,该风格的数据窗口对象可以将数据

库的内容嵌入到文本信息中,适合于开介绍信、邀请函等。创建过程中,选择了相应表

后,在随后弹出的设置属性的对话框中进行相应设置,进入数据窗口画板,然后在标题正

文及下部各区中输入文字。例如,用数据库中学生表的一些信息生成一个 RichText 风格

的数据窗口对象,形成一个邀请函,如图 5-17 所示。

图 5-17 RichText 风格的数据窗口对象

如图 5-18 所示的就是进行数据检索后,RichText 数据窗口对象的实际显示效果。

第 5 章 数据窗口

— 125 —

图 5-18 RichText 风格的数据窗口的显示例子

5.1.1.11 Graph 风格

Graph 风 格 的 数 据 窗 口 对 象 可 以 将 数 据 以 各 种 统 计 图 形 的 格 式 显 示 出 来 ,

PowerBuilder 共提供了十几种类型的统计图,如直方图、曲线图、圆饼图以及二维、三维

统计图,形式直观,易于对数据进行察看、分析。如图 5-19 所示就是一个直方图统计

图。

图 5-19 Graph 风格的数据窗口对象

5.1.2 选择数据源

数据源决定数据窗口对象从哪里以及怎样获得数据。可供使用的数据源共有 5 种类

型:Quick Select(快速选择)、SQL Select(SQL 选择)、Query(查询)、External(外部)、Stored

Procedure(存储过程)。选择数据源的窗口如图 5-20 所示。

5.1.2.1 Quick Select 数据源

Quick Select 数据源是最常用也是获取数据源最简单的方法。它允许用户直接从一个

表或多个表(视图)选取某些列。具体创建步骤如下。

(1) 在选择数据源窗口中选择 Quick Select 数据源,屏幕显示 Quick Select 对话框,如

图 5-21 所示。该对话框中列出了当前相连数据库中可供选择的表和视图的名字,选择相

Pow erBuilder9.0 基础应用与系统开发

— 126 —

应表(视图)后,Tables 栏中列出和该表有主外键关系的所有表。

图 5-20 Choose Data Source for Grid DataWindow 窗口

图 5-21 Quick Select 对话框

(2) 在 Columns 栏中选择要在数据窗口中显示的列,这些列将显示在 Comments 栏中。

(3) 在 Comments 栏中单击相应列和行,设置列的排序、检索等,也可以通过鼠标拖

动列来改变列之间的排列。可以对列设置 Ascending(升序 )、Dscending(降序 )或 No

Sorted(不排序),PowerBuilder 据此操作生成一个 SELECT 语句中的 ORDER 子句。要定

义检索条件,可以在列的 CRITERIA 框和 Or 框中输入该列要满足的条件,PowerBuilder

据此操作生成一个 SELECT 语句中的 WHERE 子句。另外,这些设置都可以在以后的程

序脚本中动态改变。

完成对数据源的定义后,进入数据窗口画板的设计状态。如果需要对数据源进行修改

或添加 SQL 复杂功能,可以选择菜单 Design|Data Source�或单击工具栏中的 SQL按钮,

第 5 章 数据窗口

— 127 —

进入 SQL Select Painter。

5.1.2.2 SQL Select 数据源

SQL Select 数据源远比 Quick Select 要灵活,它支持 SELECT 语句的所有子句,可以

为检索指定检索参数,可以对数据排序、增加计算列以及分组统计等。创建方法如下:

选择 SQL SELECT 数据源后,出现 SELECT TABLE 对话框,在其中选择一个或多个

表,之后进入 SQL SELECT 工作区,如图 5-22 所示。它使用图形方式生成完整 SQL 语句

的工具。工作区分为两部分,上面为 Table Layout 区,可以在此选择需显示的列。下面为

SQL Select 区,用户可以在该区中定义 Where、Order、Group by、Having 子句。这可以

通过选择 View 菜单中的 Where、Group、Having、Sort、Compute 的子菜单来完成。

图 5-22 SQL Select 画板

5.1.3 定义检索参数

如果要在程序运行期间动态为数据窗口提供检索参数,就必须在定义数据源时定义检

索参数,这样可使该类数据源更为灵活。选择菜单 Design|retrieval Argument,打开如图 5-

23 所示的对话框,在该对话框中定义检索参数的名称、数据类型。用按钮 Add 可定义多

个检索参数。

图 5-23 Specify Retrieval Arguments 对话框

Pow erBuilder9.0 基础应用与系统开发

— 128 —

之后,要将定义好的检索参数写入 Where 子句中。通过选择 View|Where 选项,打开

Where 子窗口,如图 5-24 所示。在 Columns 栏中选择相应字段,在 Operator 栏中选择操

作符,在 Value 栏中输入如:re_bj 的检索参数,或单击鼠标右键,在弹出菜单中选择

Argument�,然后单击 Paste,将定义好的检索参数粘贴至 Value 栏中。

图 5-24 Where 子窗口

定义了检索参数之后,程序脚本对该数据窗口对象进行检索时,可用以下语句格式进

行:

Datawindowcontrol.Retrieve(argument1,argument2,�)。

5.1.3.1 进行表连接

当定义数据源选择两个以上的表(视图)时,必须进行表连接。当两个表有主外键关系

时,PowerBuilder 会自动进行连接。如果两个表没有主外键关系,则要进行连接设置,单

击工具栏中的 Join 按钮,之后单击第 1 个表的待连接列和第 2 个表的相关列,两表间会

出现带有操作符的连线,如图 5-25 所示。如果两个表没有进行连接设置,数据窗口对象

显示数据时,将会出现不正确的结果。

Join 按钮

图 5-25 建立表连接

如果用户对 SQL 语句熟悉,可不用以上介绍的图形界面,而直接用 SQL SELECT

画板中的 SQL 编辑器对相关 SQL 语句进行修改。

5.1.3.2 Query 数据源

Query 数据源使用的是查询对象,该对象是在 Query 画板中定义的,且作为单独的对

象存在 PowerBuilder 的 PBL 库中,实际上,它是一个要频繁使用的 SELECT 语句。例

如,当一个数据库表符合一定条件的数据要在几个不同的数据窗口对象中显示时,可以定

第 5 章 数据窗口

— 129 —

义一个查询对象,在建数据窗口对象时,直接用它作为数据源即可。这样,可以不必每次

重复地选择数据库表及进行相应设置操作。

当数据源选择 Query 方式时,会出现 SELECT QUERY 对话框,选择所需 Query 文件

后,进入数据窗口画板对数据窗口对象进行相应设置操作。

5.1.3.3 External 数据源

外部数据源是数据窗口对象使用非数据库数据的惟一方法,当数据窗口对象中的数据

不是来自数据库,就要选择 External 数据源。为什么对非 DBMS 来源的数据也要使用数

据窗口对象,因为数据窗口对象的功能十分强大,拥有丰富的编辑样式、确认规则和灵活

的用户界面,所以将非 DBMS 来源的数据连接到数据窗口对象并使用该对象提供的强大

功能,是一个有效的处理办法。

指定 External 数据源后,创建向导会弹出 Define Result Set(数据源定义结果集)对话

框,如图 5-26 所示。

图 5-26 Define Result Set 对话框

用 Add 或 Insert 按钮输入每列的名称、类型及宽度,这一过程和定义数据库表很相似。之

后,进入数据窗口画板。

定义好 External 数据源后,需要在程序的脚本中为其添加数据。对应用程序中的数

据,可以直接使用 SetItem 函数,对于从文件中读取的数据,可以使用 File 簇函数或

Import 函数。

5.1.3.4 Stored Procedure 数据源

存储过程实际是在后台服务器上由数据库管理系统提供的功能。如果用户使用的数据

库支持存储过程,则可以在后台数据库中编写一个存储过程,这样就可以将它与数据窗口

对象连接,完成数据的检索和操纵。定义 Stored Procedure 数据源步骤如下。

(1) 选择 Stored Procedure 数据源后,弹出 Select Stored Procedure 对话框,如图 5-27

的所示。

(2) 在对话框中选择相连数据库中已有的存储过程,如果要显示系统存储过程,选中

System Procedure 复选框。

Pow erBuilder9.0 基础应用与系统开发

— 130 —

图 5-27 Select Stored Procedure 对话框

(3) 如果要用户自定义结果集,选中 Manual Result Set 复选框,之后会弹出对话框,

在此对话框内输入结果集的各列。

5.2 设置数据窗口

在定义了数据窗口对象之后,就可以在数据窗口画板中对它进行修饰、加工,以增强

它的功能,从而完成复杂的数据操作。

数据窗口画板界面一般分为 4 个部分,分别为左上角的设计窗口、中间的预览窗口、

左下角的列规格窗口及右侧的属性窗口,如图 5-28 所示。这些子窗口可以根据用户自己

的需要从 View 菜单中任意打开或关闭。

属性窗口

设 计 窗 口

预览 窗 口

列规格 窗 口

图 5-28 DataWindow(数据窗口画板)

第 5 章 数据窗口

— 131 —

5.2.1 设计窗口

在设计窗口区域,通常被分为几个带,它们是:标题带、细目带、汇总带和脚注带,

如图 5-29 所示。

标题带

汇总带

脚注带

细目带

图 5-29 设计窗口工作区

1. 标题带

标题带主要用来放置每页或每屏顶部显示信息,用户可以在此加入文本、时间计算

列、图像、线等内容。但对于 Grid 风格的数据窗口对象,标题带中则只能放置列标题,

而不能加入其他信息。

2. 细目带

细目带主要用来放置检索出来的数据,用户可以在此对数据进行录入或修改。数据窗

口对象中容纳的记录数由下式计算:

(数据窗口对象的高度-标题带高度-脚注带高度)/细目带高度

以上各项高度值可以通过鼠标拖动来加以改变。事实上,每次生成一个数据窗口对象

时,会根据表现风格自动地排列出细目带的内容。

3. 汇总带和脚注带

汇总带主要用来显示统计信息,该信息在所有的数据显示完毕以后才能显示。脚注带

也是用来显示一些统计信息或注释信息,但该信息将显示在每屏或每页的底部。

当选择了 Group 风格或在其他风格中选择了 Group 选项后,还会出现组标题带和组

尾带,它们分别显示各组的开头信息和各组的汇总信息。

5.2.2 属性窗口

每一个数据窗口对象中的对象元素,包括数据窗口对象本身,都有许多属性,都对应

着一个属性窗口以完成对各种属性的设置。选择 View|Properties 菜单或在要修改属性的对

象元素上单击鼠标右键后选择 Properties 项,则弹出对应的属性窗口。属性窗口中的标签

数量随所选对象元素的不同而不同。下面分别详细介绍。

Pow erBuilder9.0 基础应用与系统开发

— 132 —

5.2.2.1 数据窗口属性

数据窗口的属性窗口有 5 个标签页:General、Pointer、Print Specification、HTML

Table、HTML Generation。

1. General 标签页

General 标签页如图 5-30 所示。它主要用于设置数据窗口的计量单位、定时器时间间

隔和背景色。计量单位有 4 种选择:PowerBuilder 单位(它是系统字体平均宽度的 1/32)、

像素、1/1 000 英寸、1/1 000 厘米。

图 5-30 数据窗口对象属性窗口

内部定时器触发事件的时间间隔默认值为0,表示每分钟更新一次时间型字段,用户

重新输入数字时,计时单位为毫秒。例如,在数据窗口中动态显示时间时,若将该值设置

为 1000,则间隔 1 秒刷新数据窗口一次。

2. Pointer 标签页

Pointer 标签页主要用于设置数据窗口的鼠标指针。程序运行时,一旦鼠标进入数据

窗口区域,指针就变成用户定义的形状。

3. Print Specification 标签页

此 Print Specification 标签页主要用于设置数据窗口打印时的属性,如页面边距、纸张

大少、栏间距、栏宽等。

4. HTML Table 标签页

HTML Table 标签页用于设置数据窗口转换成 HTML 表格时的属性,如表格的边框、

单元格的宽度与缩进等。

5. HTML Generation 标签页

HTML Generation 标签页用于设置数据窗口转换成 HTML 格式时的属性,包括对应

的浏览器、HTML 版本以及是否生成 Javascript 等。

第 5 章 数据窗口

— 133 —

5.2.2.2 数据对象的属性

数据对象的属性窗口中除了常用的 5 个标签页外,还有 2 个特有的标签页:Edit(编辑

格式)和 Format(显示格式)。设置数据对象的属性是设置数据窗口对象的主要部分,其界

面如图 5-31 所示。

图 5-31 数据对象属性窗口

1. 显示格式

在数据库画板中,如果对某些列定义了显示格式,这些显示格式将被默认地继承到数

据窗口对象中来。不过在数据窗口画板中,通过在属性窗口的 Format 标签页中选择不同

的显示格式,用户还可以将列的显示风格加以修改和重新定义。定义显示格式的窗口界面

如图 5-32 所示。所有的显示格式都是通过掩码(Masks)来表示的,这些掩码通常具有特殊

的含义。PowerBuilder 支持 4 种数据类型(数字型、字符串型、日期型、时间型)的显示格

式,它们有各自的掩码。比如,在数字型的显示格式中,“#�代表 0-9 之间的任意数字,

在字符串显示格式中,“@�代表任意字符。而且,显示格式可以在程序运行中动态地改

变。如下面的例子,通过代码执行将 rxrq 字段的格式改成了新的格式。

string OldFormat, NewFormat = "mm/dd/yyyy"

OldFormat = dw_student.GetFormat("rxrq")

dw_student.SetFormat("rxrq", NewFormat)

2. 编辑风格

数据列的编辑风格可以在数据库画板中定义,在数据窗口画板中重新定义编辑风格

后,将覆盖原有的编辑风格的设置。在属性窗口中的 Edit 标签页中,选择 Style Type 下拉

列 表 框 , 可 以 选 择 如 下编 辑 风 格 : 编 辑 框 (Edit) 、 编 辑 屏 蔽 框 (EditMask) 、 复 选 框

Pow erBuilder9.0 基础应用与系统开发

— 134 —

(CheckBox)、单选按钮(RadioButton)、下拉式列表框(DropDownlistBox)、下拉数据窗口

(DropDownDataWindow)。

图 5-32 定义列的显示格式

(1) 编辑框风格

在 Style Type 下拉列表框中选择 Edit,就可以设定编辑框风格,这也是各列的默认编

辑风格,如图 5-33 所示。通过设置其中一些选项,可以控制相应列的外观和属性。

图 5-33 编辑框风格属性对话框

· Style Name 下拉列表框:用来选择用户在数据库画板中定义的编辑样式。

· style Type 下拉列表框:可在此选择不同的编辑样式。

· Format 栏:可在此输入该列的格式控制符。

· Case 下拉列表框:可在此选择该列中的字符串的大小写。

第 5 章 数据窗口

— 135 —

· Limit 栏:可在此输入限制用户可以输入的字符数。

· Accelerator 栏:用于指定加速键。

下面是一些常用复选框的具体含义。

· Auto Select:选中该项表示当移动到该列时,会自动选中列中的文本,新的输入

会覆盖原有的值。

· Display Only:选中该项表示该列仅能显示而不能被修改。

· Show focus Rectangle:选中该项表示该列得到焦点时,在该列显示一矩形框。

· Empty String is Null:选中该项表示当在该列输入空字符串时,将其设为 NULL。

· Password:选中该项时表示输入字符以*显示,以达到保密的目的。

· Required:选中该项时表示如果输入值不合法,不允许移出该列。

· Auto Horz Scroll:选中该项表示要设置水平滚动条。

· Auto Vert Scroll:选中该项表示要设置垂直滚动条。

· Case Use Code Table:选中该项时表示是否可以在窗口的下半部包含代码表。代

码表可以用来显示与数据库不同的值,即该列的显示值和存储值可以不同。

(2) 编辑屏蔽框风格

在 Style type 下拉列表框中选择 Edit Mask,将出现如图 5-34 所示界面,可以在此设

置屏蔽框风格。编辑屏蔽框风格可以用来强制设定数据显示和输入的格式。如对某一日期

型字段,必须统一以 mm/dd/yyyy 格式来输入,可以方便地用该编辑风格实现。如果选中

了 Spin Control 复选框,并指定变化范围(Spin Min、Spin Max)、步长(Spin Increment),相

应的列会带一对上、下箭头,单击可调整数据。图 5-34 中所示情况是将“入学日期�设置

为 mm/dd/yyyy 格式,即显示效果为 01/09/1999。 (3) 复选框风格 当某一列可以取两个值或三个值中的一个时,可以把该列定义为复选框风格。例如,

“学习形式”列有两种取值:“脱产”或“不脱产”,则可用复选框来表示。在 Style Type下拉列表框中选择 Checkbox,将出现如图 5-35 所示界面。

图 5-34 编辑屏蔽框风格属性对话框 图 5-35 复选框编辑风格属性对话框

Pow erBuilder9.0 基础应用与系统开发

— 136 —

其中一些属性的含义如下。

· Text 文本框:在其中输入的字符将显示在复选框旁。

· Data Value for ON(Off)文本框:在此输入复选框在选中(未选中)时,相应列要存储

的值。

· States 复选框:选中它,可使复选框有第 3 种(即变灰)状态。

(4) 单选按钮风格

在 Style Type 栏中选择 Radiobutton,进行单选按钮风格设置,其界面如图 5-36 所

示。如果一列用到少数几个可能的值,使用单选按钮风格将十分方便。例如,将“性别�列设为该编辑风格。

图 5-36 单选按钮编辑风格属性对话框

单选按钮编辑风格的几个属性含义如下。 · Columns Acrosss 框:在其中输入一行中所要显示的单选按钮的个数。 · Scale Circles 复选框:选择单选按钮的表现形式。 在 Code Table(代码表)中有两栏如下。 · Display Value 栏:输入在单选按钮旁边显示的信息。 · Data Value 栏:输入单选按钮实际代表的值,也就是对应的存储在数据库中的值。 (4) 下拉列表框编辑风格 在 Style Type 下拉列表框中选择 DropDownListBox,进行下拉列表框设置,出现如图

5-37 所示界面。使用下拉列表框编辑风格后,在录入数据时就不必再输入文字,而只要

从弹出的下拉式列表框中选择一个值即可。它与单选按钮类似,只是表现形式不同,在选

择的数据较多时,下拉列表框可以比单选按钮节省屏幕空间。例如,将“专业名称”列设

为该编辑风格,在录入数据时仅需从下拉列表框中选择一个即可。 这种编辑风格下要使用 Code Table(代码表)。在代码表的 Display Value 栏中输入用来

显示的文本,在 Data Value 栏中输入存储在数据库中的值。在程序执行过程中,可以通过

脚本来控制这个代码表。可用 Getvalue()函数来获得某一列的值,用 Clearvalue()函数来清

除某一列代码表的值。此外还有几个该编辑风格特有的复选框。 · Allow Editing:选中该项表示允许用户在下拉列表框中的编辑区中输入数据,即

用户可以不必从列表框中选择数据,而自己输入值。

第 5 章 数据窗口

— 137 —

图 5-37 下拉列表框编辑风格属性对话框

· Sorted:选中该项表示使下拉列表框中的数据以数字和字母排序。

· Always Show List:选中该项表示使下拉列表框随焦点移入而自动下拉。

· Always Show Arrow:选中该项表示在下拉列表框的尾部显示下拉箭头标志。

(5) 下拉式数据窗口风格

下拉式数据窗口风格与下拉式列表框风格的显示效果是一样的,它们的不同之处在于

下拉列表框的数据来自代码表,是静态的。而下拉式数据窗口的列表框数据来自数据库,

其值随数据库中数据的修改和维护而动态改变。例如,将“班级名称”设为该编辑风格,

在 Style Type 栏中选择 DropdownDW,其下拉数据窗口的数据来自一个班级数据窗口对象

(d_class),显示值来自“班级名称”(bjmc),存储值为“班级代码”(bjdm),图 5-38 给出

了设置后的界面。

图 5-38 下拉式数据窗口编辑风格属性对话框

Pow erBuilder9.0 基础应用与系统开发

— 138 —

设置属性时,在 DataWindow 栏中输入数据窗口名称或单击右边的 按钮选择数据

窗口,当然,必须将为该列提供数据源的数据窗口对象提前建好。之后,在 Display

Column 下拉列表框中选择用来显示的列,在 Data Column 下拉式列表框中选择用来存储

的列。

当用下拉式数据窗口编辑风格时,要比其他编辑风格复杂一些,给列提供数据源的数

据窗口,即子数据窗口对象一定要用 SetTransObject()和 Retrieve()进行组装。在组装前,

还要用 GetChild()函数获得子数据窗口对象的引用,否则下拉数据窗口中不会有显示数

据。

下例要实现在一个带有下拉数据窗口编辑风格列的数据窗口的 ItemChanged 事件中,

当选择一个专业时,将会显示出这个专业相应的班级。在数据窗口中将班级名称列设为下

拉式数据窗口编辑风格,子数据窗口对象是以 SQL Select 为数据源的,带有一个检索参

数,其值是当前选择的专业代码,具体脚本代码如下:

DataWindowChild ldwc_bjmc //子数据窗口对象的数据类型为 DataWindowChild

Integer li_return,li_zydm

IF dwo.name="zydm"THEN//句中 dwo 是 ItemChanged 事件的一个参数,它是对当前数据窗

口的引用,判断是否部门号的列的值发生了改变

li_return=dwo.GetChild("bjmc",ldwc_bjmc)

IF li_return=-1 THEN

Messagebox("错误! ","这不是一个子数据窗口! ")

RETURN

END IF

li_zydm=ingeger(data)

ldwc_bjmc.SetTransObject(SQLCA) //完成对子数据窗口的组装,使其正常显示出相应专业

的班级

ldwc_bjmc.Retrieve(li_zydm)

END IF

图 5-39 所示的数据窗口就是综合应用了不同的编辑风格后的显示效果。其中,“性

别”列应用了单选按钮编辑风格;“班级名称”列应用了下拉数据窗口编辑风格;“学习形

式”列应用了复选框编辑风格;“入学日期”列应用了编辑屏蔽框编辑风格。

图 5-39 一个综合应用了不同编辑风格的数据窗口对象

第 5 章 数据窗口

— 139 —

5.3 数据的处理

通过上节介绍而建立和设置的数据窗口对象有时还不能满足要求,还要对数据窗口对

象的数据进行处理,如对要显示的数据进行检索、过滤、排序、分组,或决定数据窗口对

象在数据发生了改变后,以怎样的方式去更新对应数据库表,以及怎样处理数据窗口的错

误。下面将予以介绍。

5.3.1 数据检索

在数据窗口对象中要检索出数据并加以显示,一般在数据窗口画板中的 Preview 子窗

口窗口中单击鼠标右键,在弹出菜单中选择 Retrieve 即可。在程序运行中对数据窗口进行

检索,则可用 Dataobjectcontrol.Retrieve()命令来实现。

如果数据窗口对象每次检索的数据量很大,可能检索时间会很长。因此,可以定义数

据窗口对象每次只从数据库中检索所需的数据,以便能提高效率。只检索所需数据的设置

很简单,选择 Rows|Retrieve 子菜单中的 Rows AS Needed 项即可。

还可以定义每次把检索出的数据存储到硬盘的临时文件,而不是存储在内存中。选择

Rows|Retrieve 子菜单中的 Rows To Disk 项可完成此设置。定义了该属性后,在程序运行

时,PowerBuilder 将根据用户的指令在内存和临时文件之间交换数据,这会节省内存。当

然,由于检索时要进行读写硬盘操作,故在执行速度上略受影响。

5.3.2 数据排序

在定义 SQL Select 数据源时,Select 语句中可以事先定义好排序语句,当数据被取到

数据窗口对象时,它们已是排好序的。在数据窗口画板中,也可以对显示数据的排序进行

重新设定,而且能完成 Select 语句中无法描述的更为复杂的排序条件表达式。具体实现步

骤如下。

(1) 在数据窗口画板中选取 Row|Sort 菜单项,会弹出如图 5-40 所示对话框。

图 5-40 Specify Sort Columns 对话框

(2) 把要排序的列从左边的列表框中拖动到右边列表框中,选中 Ascending 复选框,以

Pow erBuilder9.0 基础应用与系统开发

— 140 —

确定是升序还是降序。

(3) 如果要改变排序中列的优先级,可以上下拖动各列到适当的位置。

(4) 如果要按某列或多列的表达式排序,可以双击右列表框中的相应列,将弹出

Modify Expression 对话框,在此可输入需要的表达式。

如果要在程序中对数据窗口动态设定排序条件实现动态排序,可在脚本中用 SetSort()

和 Sort()函数实现。

5.3.3 数据过滤

有时需要对同一批数据从不同的角度进行分析,即要按一定条件抽取数据。此时,可

以对数据窗口对象中要显示的数据进行过滤。过滤并不影响所取到的数据,被滤去的数据

仍保存在内存中(即在过滤缓冲区中),只不过没有显示出来。具体实现步骤如下。

(1) 在数据窗口画板中选择 Rows|Filter 菜单项,出现 Specify Filter(定义过滤)对话

框,如图 5-41 所示。

图 5-41 Specify Filter 对话框

(2) 输入每一行进行检查时所依据的逻辑表达式,表达式为真,则该行被显示出来,

否则该行将被滤掉。在 Functions 列表框中的函数和在 Columns 列表框中的列名,可以被

方便地粘贴到表达式中。

要在程序运行中对数据窗口实现动态过滤,可用 SetFilter()函数和 Filter()函数来完

成。

5.3.4 数据分组

可以把相关的数据分为一组,也可以对每组数据进行统计分析。分组是以数据窗口中

的一列或多列为依据的。当在选择了 SQL Select 数据源时,也可以为其 Select 语句中的

Group 子句设定分组条件。但在数据窗口画板中进行数据分组更为方便,设置步骤如下。

(1) 选择 Rows|Create group 子菜单,出现 Specify Group Columns(定义分组列)对话

框,如图 5-42 所示。

(2) 在对话框中,将分组依据的列从左边列表框中拖动到右边的列表框。

设置完毕后,返回数据窗口,可以发现设计窗口自动添加了相对应的组标题带和组尾

带,同时属性窗口自动切换到相应的标题属性处,如图 5-43 所示。在此对话框内的属性

第 5 章 数据窗口

— 141 —

含义如下。

图 5-42 Specify Group Columns 对话框 图 5-43 标题属性对话框

· Group Definition:选择分组依据的数据列。

· Group Sort:选择用于排序的组。

· Reset Page Count 复选框:选中该项,允许重设每组页码。

· New Page On Group Break 复选框:选中该项,使每组均从新页开始。

分组设置后,通常要在组尾区提供一些诸如组内小计之类的计算列。选择菜单

Insert|Control|Computed Column,然后在欲放置计算列的位置单击,在出现的编辑对话框

中,输入计算公式。比如,要对组的成绩列进行求平均值运算,表达式为:Avg(cj1 for

group1),如果要对全部成绩列求平均值,表达式则为:Avg(cj1 for all)。

此外,分组时,可设置同一组中某些有重复值的列只显示一次数据,其他数据则被屏

蔽掉,这会使数据窗口显示的数据简洁明了。选择菜单 Row|Suppress Repeating Values,

在弹出的 Specify Repeating Value Suppression List 对话框中,将要显示一次数据的列拖动

到右边的栏中即可。

图 5-44 给出的数据显示窗口设定了以“班级代码”列为分组,在组尾区加入了一个

成绩平均值的计算列,并对“班级代码”、“专业名称”列进行了屏蔽设置,即一个分组只

显示一次班级名称和专业名称。

图 5-44 一个设定分组并进行数据屏蔽的数据窗口

Pow erBuilder9.0 基础应用与系统开发

— 142 —

5.3.5 数据更新

在创建数据窗口对象时,PowerBuilder 自动设置默认的数据库更新属性。一般在单用

户环境下,默认的更新属性能够满足要求。但是在多表操作或多用户环境下,就应该重新

设置更新属性,以保证对数据库的修改不会破坏数据库的安全性、完整性以及一致性。简

单说,设置更新属性,就是决定数据窗口中的数据是否可更新至数据库,并决定在更新数

据窗口中数据时生成怎样的 SQL 语句。对数据窗口中数据的任何改动和操作,最后都生

成相应的 SQL 语句,并加以执行,完成对数据库的修改。选择 Rows|Update Properties 子

菜单项,出现设定更新属性的对话框,如图 5-45 所示。

图 5-45 Specify Update Properties(设定更新属性)对话框

1. Allow Updates 复选框

此复选框决定 Table to Update 下拉列表框中的表是否可更新,如果数据窗口对象是根

据一个表创建的,并且包含了主键列,此选项默认为选中。当数据窗口对象是依据两个表

或更多表创建的,每次进行更新操作也只能更新一个表,若想更新包含的所有表,则必须

在程序脚本中编写相应的代码,基本原理是:先将一个表的更新属性设为可更新,其余表

设为不可更新,进行一次更新操作,之后,再将另一个表更新属性设为可更新,其余表设

为不可更新,重复进行一次更新操作。有几个表,就要执行几次更新操作。更新属性可以

通过有关函数在程序运行时动态设定。

2. Where Clause For Update/delete 栏

该栏有 3 个选项,以决定在数据窗口更新时如何生成 SQL 语句的 Where 子句,以对

数据库实现并发性控制。假设,一个用户从学生信息数据库表(t_student)中取出学号(xh)为

“99101001”的一条记录,并将其数学成绩(cj1)由“87”改为“80”,此时,又有一用户

取出相同一条记录,并对班级代码(bjdm)从“计 9901”改为“法 9902”。那么,选择不同

的选项,会对两用户的修改操作产生什么不同,下面将分别阐述。

(1) Key Columns

选择了 Key Columns 后,PowerBuilder 创建 SQL 语句对该行检索出的原始键值与数

据库中键值进行比较。如果关键值相匹配,则可以对数据库进行修改。这种选项一般适用

第 5 章 数据窗口

— 143 —

单用户情况。相应地,当第 2 个用户要修改数据时,将生成如下 SQL 语句:

Update t_student set bjdm="法 9902" where xh="99101001"

可以发现,此修改操作会成功实现。

(2) Key and Updatable Columns

当选择了 Key and Updatable Columns 选项后,PowerBuilder 创建 SQL 语句对检索出

的初始键值以及所有初始检索出的可修改的列与数据库中的相应值进行比较。如果匹配,

则允许修改。这种手段提供了最大限度的并发性的修改检查。相应地,当第 2 个用户要修

改班级代码时,会生成如下 SQL 语句:

Update t_student set bjdm="法 9902" where xh="99101001"and cj1=87 and

bjdm="计 9901"

因为,第 1 个用户修改了数学成绩的值,所以上面的 Where 子句的条件是不成立的

(即“cj1=87”条件不成立),因此修改不会成功。这种模式一般应用在多用户的情况下,

检查最严格,从而实现了并发性控制。

(3) Key and Modified Columns

如果选择了该项,PowerBuilder 创建 SQL 语句对检索出的初始键值和所有已修改的

列与数据库的相应列的值进行比较,如果这些值匹配,就可以修改。这种手段提供了最大

限度的并发性。在上面的例子中,会生成如下 SQL 语句:

Update t_student set bjdm="法 9902" where xh="99101001" and bjdm="计

9901"

可以看出,此修改会成功执行。

3. Key Modification

该栏内的两个单选项决定当惟一键列发生改变时,应生成何种 SQL 语句,也就是定

义更新主键的方式。注意,这些选项只在用户修改了关键值列并且所使用的 DBMS 允许

在 Update 语句中对主键进行修改时才有效。

(1) Use Delete then Insert

选中此选项后,当关键值列修改时,相应数据行先被删除,再插入新的键值。如果使

用的数据库可以自动地连带删除,就不需要这个选项。

(2) Use Update

当数据更新前,只有一行数据可被修改时,选择此项。注意,有些 DBMS 不支持这

种形式的修改。

4. Updateable Columns 列表框和 Unique Key columns 列表框

当选中 Updateable Columns 列表框中的某列时,该列就可以被更新。Unique Key

columns 列表框中选定表中的惟一键列,默认为主键列。

5. Identity Column

Identity Column(标识列)是一个在数据库表中能够由 DBMS 自动产生惟一值的数字类

Pow erBuilder9.0 基础应用与系统开发

— 144 —

型的数据列,但并不是所有 DBMS 都支持这种技术。当插入一个新记录行时,被选定为

Identity Column 的列将不被包括在 Insert 语句中,而且在数据窗口中也不能对该列进行修

改。

5.3.6 数据的校验

用户将修改或录入的数据提交给数据库之前,必须通过数据的合法性和有效性检验。

数据的有效性检验可以在创建数据库时设置,这些设置将被默认地置入创建的数据窗口对

象中。用户可以在数据窗口画板中,重新指定检验规则。在介绍数据校验之前,先介绍一

下编辑框中的数据、外在表现的数据和数据窗口缓冲区中的数据。编辑框中的数据就是在

窗口中数据窗口控件中的数据,它是被用户刚输入到数据窗口控件中的数据,这时焦点还

未 离 开 该 列 对 应 的 编 辑 框 。 数 据 窗 口 缓 冲 区 是 数 据 窗 口 技 术 的 一 个 重 要 机 制 ,

PowerBuilder 在将数据从数据库取出并在数据窗口显示之前,先将数据放在称之为数据窗

口缓冲区的内存中,数据窗口显示的数据都是从缓冲区中取来的。数据窗口外在表现的数

据则是处于以上两种数据之间。它们之间的关系可用图 5-46 表示。

编辑框

数据窗口外在表现

数据库

数据窗口缓冲区

图 5-46 数据窗口控件中数据的 3 种表现

在数据窗口画板中指定校验规则步骤如下。

(1) 选择要指定校验规则的列,在对应的属性窗口中,会发现在有些属性的旁边有一

个红色的小按钮,单击此按钮,将弹出对该属性的校验规则对话框,如图 5-47 所示。

图 5-47 校验规则对话框

第 5 章 数据窗口

— 145 —

(2) 在 Epression 编辑框中输入和编辑校验规则,可利用界面中提供的运算符,从

Functions 栏中和 Columns 栏中选择粘贴各种函数和列名来组成校验表达式。设置好后,

可以单击 Verify 按钮,检查校验表达式在语法上的正确性。

(3) 单击 OK 按钮,返回属性窗口,先前的红色小按钮变为绿色。

当数据从编辑控件移动存储到缓冲区中成为数据项时,PowerBuilder 将进行检验。当

用户在编辑框中输入一个或多个字符后(触发一个 EditChanged 事件)或用户按下 Enter

键、改变焦点到数据窗口控件中不同的列、在脚本中执行了 Accepttext()函数的情况下,

则开始有效性检验,检验有 4 个层次。

第 1 层:编辑框中的数据是否发生了变化。对编辑控件的数据首先检查编辑框的数据

有无变化,如果编辑框中的数据是 100,用户擦掉它,重新输入 100,数据窗口不会做任

何动作,即不会把这个值传给数据窗口缓冲区。

第 2 层:检验输入的数据类型是否正确。如果数据改变了,数据窗口将检查新输入的

数据类型是否正确。如果不正确,将触发 ItemError 事件,在此事件的脚本中,可编写相

应代码对错误进行处理。如果数据类型正确,转向第 3 层检验。

第 3 层:检验数据是否符合检验规则。编辑框中的数据与设置的检验规则进行比较,

如果数据不符合检验规则,则触发 ItemError 事件,否则,进行第 4 层检验。

第 4 层:检验数据是否通过 ItemChanged 事件。在这里,PowerBuilder 又提供了一种

检验数据的方法,当数据窗口的 ItemChanged 事件中有对数据检验的代码时,将执行这段

代码。如果没通过检验,仍然产生 ItemError 事件。

当 4 层检验都通过以后,编辑框中的数据将被存到数据窗口的缓冲区中,但此时,数

据仍没有真正存储到数据库中,只有当正确执行了 Update()命令后,缓冲区的数据才被更

新到数据库中。

5.3.6.1 处理数据窗口的错误

在应用程序的执行过程中,由于对数据窗口的不正确的设定,可能会触发数据窗口的

错误事件。在数据窗口中或嵌入式 SQL 语句中对数据库的不正确访问、访问失败,也会

导致相应事件的发生。下面介绍如何捕捉这些事件,并在这些事件的脚本中编写相应的代

码来处理错误。

5.3.6.2 数据窗口的 Error 事件

当引用一个不存在的对象、拼错属性名称或引用不存在的数据时,将触发数据窗口控

件的 Error 事件。表 5-1 给出 Error 事件的参数及描述,它们中记录了错误的信息,用户

可以通过查看这些信息而定位错误。

表 5-1 Error 事件的参数及描述

参 数 类 型 含 义 ErrorNumber Unsigned integer 错误号,每一个错误号都代表一种类型的错误ErrorText String 错误的原因描述

ErrorWindowMenu String 错误发生时所在的窗口或菜单Errorobject String 错误发生时所在的对象名ErrorScript String 错误发生时所在脚本名

Pow erBuilder9.0 基础应用与系统开发

— 146 —

(续表)

参 数 类 型 含 义 ErrorLine Unsigned integer 错误发生时所在的行号Action ExceptionAction 错误发生后应用程序的处理方式

Returnvalue Any 返回值

其 中 Action 参 数 有 3 种 取 值 : ExceptionFail! 、 ExceptionIgnore! 、

ExceptionSubstituReturnvalue!,分别表示允许错误发生、忽略错误继续执行、替代返回

值。Returnvalue 参数只有在 Action 参数设为 ExceptionSubstituReturnvalue!时才能使用,

以使 Error 事件返回不同的值。注意,在 Error 事件的脚本中不能用 Return 返回值。

当发生触发 Error 事件后,如果没有在该事件的脚本中编写处理代码,应用程序将触

发 SystemError 事件,默认情况下,将显示错误信息,并终止应用程序。一般要在

SystemErrorr 事件的脚本中,编写处理代码,而出错信息可以从 Error 对象中获得。下面

给出 SystemError 事件脚本中处理错误的代码。在脚本中打开了一个错误信息显示窗口,

如图 5-48 所示。窗口中有一个数据窗口控件(dw_error),有 3 个决定进一步操作的按钮。

Open(w_system_error)

图 5-48 一个显示错误的定制窗口

在 w_system_error 窗口的 open 事件中有如下代码:

string ls_error

dw_error.insertrow (1)

//通过引用 Error 对象,获得错误信息,并插入数据窗口中以显示出来

dw_error.setitem (1,"errornum",string(error.number))

dw_error.setitem (1,"where" ,error.windowmenu)

dw_error.setitem (1,"object" ,error.object)

dw_error.setitem (1,"event" ,error.objectevent)

dw_error.setitem (1,"line" ,string(error.line))

//将 Error 对象中的错误原因描述转换成易于理解的中文

Choose Case error.text

Case "Divide by zero"

Ls_error="被零除出错"

Case "Null object reference"

Ls_error="空对象"

Case "Array boundary exceeded"

Ls_error="数组越界错误"

第 5 章 数据窗口

— 147 —

//还可以添加更多错误情况

⋯⋯

⋯⋯

⋯⋯

End choose

if len(ls_error)>1 then

dw_error.SetItem (1,"message" ,ls_error)

else

dw_error.SetItem(1,"message",error.text)

end if

5.3.6.3 处理数据库错误

在执行嵌入式 SQL 语句,如 Connect、Disconnect、Commit、Rollback,或调用数据

窗口的 Retrieve()和 Update()函数时,都有可能发生数据库错误。而这些错误必须要加以

检测、处理,从而保证对数据库的操作及控制。

1. 检查 SQLcode 属性

当执行了 Connect、Commit 等 SQL 语句后,要及时地检测操作是否成功。这一般通

过检查 SQLcode 的值来完成。SQLcode 的值有 3 个:0 表示执行正确、-1 表示执行有错

误、100 表示执行后结果集中无数据。下面是脚本中常用的检错代码:

Connect Using SQLCA;

If SQLCA.SQLcode<>0 Then

MessageBox("警告"," 数据库连接失败!",StopSign!)

HALT Close

End if

2. 检查调用函数的返回值

当调用 Retrieve()和 Update()函数之后,可以通过检查函数的返回值,以确定函数的

执行情况。下面是这两个函数的返回值情况。

Retrieve()函数: >=1 表示检索成功并返回检索到的记录数。

-1 表示检索失败,DBError 事件被触发。

0 表示没有检索到数据。

Update()函数:1 表示更新成功。

-1 表示更新失败,DBError 事件被触发。

下面是调用更新和检索函数并进行相应检验的代码例子:

Long ll_count

If dw_1.Update()=1 Then

//更新函数正确执行后继续执行检索操作

ll_count=dw_1.Retrieve()

If ll_count=-1 then

MessageBox("警告"," 检索失败!",StopSign!)

Elseif ll_count=0 then

Pow erBuilder9.0 基础应用与系统开发

— 148 —

Dw_1.title="没有检索到数据!"

Else

Dw_1.title="共检索到"+string(ll_count) +"条记录!"

End if

Else

//更新函数更新失败

MessageBox("警告","更新操作没有成功!")

End if

5.3.6.4 数据窗口的 DBError 事件

当调用 Update()函数和 Retrieve()函数没有成功时,则触发 DBError 事件。默认情况

下,将显示一个消息框显示数据库错误代码和信息。如果不想此消息框出现,可以在脚本

中用改变返回值,即写下面一句即可。

Return 1

可以在 DBError 的脚本中,编写自己的代码显示明确的错误信息以及对错误进行处

理。错误信息可以从 DBError 事件的参数中获得。表 5-2 给出 DBError 事件的参数及描

述。

表 5-2 DBError 事件的参数及描述

参 数 描 述 SQLDBcode DBMS 提供的错误代码SQLErrText DBMS 提供的错误信息

SQLSyntax 传递给 DBMS 的 SQL 语句Buffer 错误数据所在的缓存区Row 发生错误数据的行号

5.3.7 在数据窗口对象中添加对象

在数据窗口中还可以根据需要添加新的对象,以丰富数据窗口的外观和功能。这些对

象包括:列、文本框、绘图对象、按钮、位图、计算域、统计图、OLE 对象和报表等。

每当增加一个新的对象以后,都会有一个相应的为这个对象设置属性的属性窗口。

5.3.7.1 添加和删除列对象

在数据窗口画板中,可以添加列,也可以删除列,而且可以使一个列重复显示多次。

选择 Design|Data Source 子菜单,进入数据窗口的数据源画板,在此可以对在数据窗口中

显示的列进行增加或删除。如果在这里将某列删除了,在数据窗口画板中,将不能再增加

这一列,因为它已从结果集中删除了。

在数据窗口画板中添加列的步骤如下。

(1) 在 Insert|Control 菜单中选择 Column 项。

(2) 单击要添加列对象的位置,弹出 Select Column 对话框,列出了结果集中所有的

列对象。

(3) 选择要增加的列,单击 OK 按钮。

第 5 章 数据窗口

— 149 —

在数据窗口画板中,能增加结果集中的列。删除列只是将列对象从数据窗口中移走,

并未从结果集中删除。

5.3.7.2 添加计算域

首先,要注意区分计算域和计算列,在数据窗口中它们是不同的对象。计算列是在创

建数据窗口时定义的,由 DBMS 计算。当数据检索到数据窗口后,无论对其他列做怎样

的修改,都不会影响计算列的值,它依据的是数据库中的数据。而计算域则依据数据窗口

缓冲区中的数据,当数据窗口中的数据发生改变时,计算域的值就会随着改变,它是由

PowerBuilder 计算的。计算域的添加步骤如下。

(1) 在 Insert|Control 菜单中选择 Computed Field 项。

(2) 单击要添加列对象的位置,弹出公式编辑对话框,在此输入计算域的表达式。

(3) 在属性对话框的 Name 栏中输入计算域的名称,以便于在程序代码中引用,并根

据需要设置其他属性。

在 Insert|Control 子菜单中提供了一些常用的计算域,如 Sum(求和)、Average(求平均

值)、Today(当天日期)、Count(求列)、Page of n(页码)等。如果要添加这些计算域,直接

进行选择即可。

5.3.7.3 添加静态文本框

在默认情况下,数据窗口对象中放置一些文本对象,如列标题或标签。有时用户还需

要为新增加的列加标题,或在数据窗口对象中添加一些文本性的说明文字,这都需要通过

添加静态文本框来完成。添加静态文本框的步骤如下。

(1) 在 Insert|Control 菜单中选择 Text 项。

(2) 单击要添加文本对象的位置,然后修改对应的属性窗口中的 Text 编辑框文本。

(3) 设置字体、对齐方式等属性。

5.3.7.4 添加按钮对象

在数据窗口中可以添加按钮对象,就像在窗口中添加按钮对象一样。不同的是,在数

据窗口的按钮对象可以使用已定义好的许多默认功能,诸如翻页、插入记录、删除记录

等,无须编写额外的代码,也可以自定义完成其他功能,所以,能加快开发进度。

添加一个按钮对象的步骤如下。

(1) 在 Insert|control 菜单项中选择 Button 项,单击需添加的位置。

(2) 在出现的如图 5-49 所示的属性窗口设置属性。各属性的含义如下。

· Text 编辑栏:其中输入的文字将成为按钮对象上显示的文本。

· Suppres Event 复选框:选中该框时,表示按钮只执行 Action 下拉列表框中指定的

动作,不会触发数据窗口控件的 ButtonClicking 和 ButtonClicked 事件。

· Action 下拉列表框:在这里选择 PowerBuilder 为对应按钮提供的默认功能。

Append Row:在数据窗口最后一行后增加一行记录。

Insert Row:在当前行插入一行记录。

Filter:打开定义过滤条件对话框,在用户指定过滤条件后,显示符合条件的记录。

Pow erBuilder9.0 基础应用与系统开发

— 150 —

图 5-49 按钮对象属性窗口

Page Next:向下滚动一页。

Query Mode:以查询模式显示数据窗口,用户可以定义查询条件以查询数据。

User Defined:用户自定义按钮功能。

设置完后,通过预览数据窗口,可出现如图 5-50 所示的类似样式。其中所有添加的

按钮对象均使用默认的功能。

图 5-50 有按钮对象的数据窗口

5.3.7.5 添加绘图对象

为了数据窗口的美观,可以添加一些绘图对象以修饰数据窗口外观。绘图对象包括:

Line(线段)、Oval(椭圆)、Rectangle(矩形)、Round Rectangle(圆角矩形)。添加的方法与添

加文本框的方法相同。在属性窗口中可以设置对象的颜色、线型等属性。

5.3.7.6 添加图片对象

图片对象可以添加到数据窗口的任意位置以增强数据窗口的美观。添加步骤如下。

(1) 在 Insert|Control 菜单中选择 Picture 项。

(2) 单击放图片的位置,出现图片对象属性窗口,在此输入图片的文件名称。

(3) 设置 Original Size、Invert Image 属性,决定是否按原始尺寸显示图片、是否翻转

第 5 章 数据窗口

— 151 —

图片的颜色。

5.3.7.7 添加图表对象

图表对象可用于显示数据统计结果,它可以用各种曲线、直方图、饼图来表现数据。

创建步骤如下。

(1) 在 Insert|Control 菜单中选择 Graph 项,单击设计窗口要添加的位置,弹出设置图

表数据对话框,如图 5-51 所示。

图 5-51 图表数据对话框

(2) 在对话框中,设置相应属性。下面是各属性的具体含义。

· Row 下拉列表框:在其中选择图表的数据范围。All 选项表示在图表中显示数据

源中的所有行;Page 选项表示显示当前页的所有行;Group 选项表示显示当前组里的行。

· Category 下拉列表框:在其中选择要作为图表类别的数据列,即横坐标,也可以

输入表达式。

· Value 下拉列表框:在其中选择要在图表中显示的统计数据列,可以选择多列。

· Series 复选框:决定图表是单系列形式还是多系列形式。

5.3.7.8 添加 OLE 对象

在数据窗口中可以添加 OLE 对象。使数据窗口可以借助其他 Windows 应用程序处

理、显示数据。添加步骤与添加其他对象的方法相同,只是要根据不同的 OLE 对象进行

相应的设置。

5.3.7.9 添加报表

在数据窗口中还可以添加报表,也就是可以在一个数据窗口对象中再加入一个或多个

数据窗口对象,就形成了嵌套式报表。这些数据窗口对象可以相关,也可以不相关。但更

多的情况下,是建立主从式报表,即报表之间有相关的列。下面的一个具体例子说明添加

报表的步骤。

(1) 在 Insert|Control 菜单中选择 Report 项,单击要添加的位置,弹出 Select Report 对

Pow erBuilder9.0 基础应用与系统开发

— 152 —

话框。当建立主从式报表时,一般将从报表放在设计窗口的细目带,细目带可以随从报表

数据行的多少自动调整高度。

(2) 选择对话框中要添加的数据窗口对象,设计窗口将出现如图 5-52 所示的样式。在

显示班级信息数据窗口对象中,添加了一个学生信息数据窗口对象,它随班级的改变而变

化该班学生的信息。在此,添加的数据窗口对象的列信息是不可见的。

图 5-52 添加报表后的数据窗口对象

(3) 如果添加的报表与基报表有相关性,为将两个数据对象关联起来,最有效的方法

就是使用检索参数。当然,要保证添加的数据窗口对象已定义了检索参数。单击刚添加的

数据窗口对象,在对应的属性窗口中的 Arguments 栏中,设置检索参数的来源。同时,设

置其他属性,界面如图 5-53 所示。

图 5-53 嵌套式报表的属性对话框

设置完毕后,预览该数据窗口对象,显示结果如图 5-54 所示。需要注意的是,在嵌

套式报表中的数据是不可编辑的,但在程序脚本中,可用 GetChild()函数对获取从报表的

数据信息进行修改。

第 5 章 数据窗口

— 153 —

图 5-54 一个嵌套式报表的显示结果

当数据窗口中的所有对象都添加完毕后,另一个注意的问题是各个对象焦点获得的次

序,也就是它们的 Tab 值(Tab Order)。它决定了在程序运行时,当用 Tab 键进行各个对象

间的切换的顺序。PowerBuilder 默认的 Tab 值顺序是各个对象从左到右、从上到下以 10

为单位依次增加。用户可以通过选择 Format|Tab order 菜单项,重新设置 Tab 值。当设

Tab 值成 0 时,将使该对象不能得到焦点,从而也就不可编辑。Tab 值越小越先得到焦

点。在程序运行中,也可以通过 SetTabOrder()函数动态设置对象的 Tab 值。

5.4 数据存储

5.4.1 数据存储简介

5.4.1.1 数据存储的概念

数据存储(Data Store)对象是一个不可视的数据窗口控件。它除了一些可视的特性外,

使用方法与数据窗口控件基本相同。数据存储也有一个与之相连的数据窗口对象,它主要

用于不需要可视的用户对象,但又要利用数据窗口对象的功能的情况。由于数据存储不需

要额外的显示开销,因此数据存储的运行效率比使用数据窗口对象时高得多。

数据窗口控件的大部分使用方法也同样适用于数据存储。比如,数据存储同样支持

Retrieve()、Update()、Insertrow()、Deleterow()、Rowcount()等函数。但是,数据存储不支

持一些可视性方面的函数,如:SetFocus()、Indicator()、GetclickedRow()等处理用户输入

的函数。因此,一个数据窗口控件和一个数据存储不同之处在于:数据窗口控件必须作为

一个控件存在于某个窗口中,需要有可视的特性,而数据存储作为一个独立的对象,是不

需要的。

5.4.1.2 数据存储的功能

当用户不关心在屏幕上如何显示数据的处理过程和结果,而数据又来自数据库时,就

可以考虑使用数据存储。数据存储可以完成以下 4 个功能。

Pow erBuilder9.0 基础应用与系统开发

— 154 —

1. 进行后台数据库处理

利用数据存储可以在后台进行数据库处理,而不用通过隐藏数据窗口控件来实现。

2. 为多个视图服务

一个数据存储对象中的数据可以为一个或多个数据窗口控件共享。因此,当一个窗口

需要为同一信息提供多个视图时,可以利用数据存储来存储所需要显示的数据,另外,用

户也可以通过程序的代码直接访问数据存储的数据。

3. 不需要使用嵌入式 SQL 来操纵表中的记录

因为,数据存储的处理数据速度一般要比执行嵌入式 SQL 语句快,所以当需要数据

库记录操作,并将结果显示在屏幕上时,可以使用数据存储来处理相应的数据库操作,而

不要执行嵌入式 SQL 语句。

4. 在分布式应用服务器上进行数据库访问

在分布式环境中使用数据存储时,位于服务器上的事务逻辑可以利用数据存储与数据

库打交道。数据存储可以充分利用服务器上的计算机资源,为每个客户执行所需要的数据

库操作。

5.4.2 使用数据存储

使用数据存储时,必须先在程序脚本中为这个数据存储进行定义,然后创建一个数据

存储的实例,对该实例赋一个数据窗口对象,这样就可以对数据进行各种处理和操作了。

最后,要删除这个数据存储,释放占用的内存资源。

5.4.2.1 数据存储对象的属性

数据存储和数据窗口控件一样,也有一个编辑控件,但在它们之间存在一些不同的地

方。用户可以直接修改数据窗口控件的编辑控件的内容,但不能直接修改数据存储的编辑

控件的内容,只能通过程序脚本来实现对其的维护。数据存储对象只有两个属性。

(1) DataObject:指定与其相关联的数据窗口对象的名称,数据类型为 String。

(2) Object:允许用户在程序中使用存在于数据窗口对象内部的对象,比如:Column

或 Text 对象,数据类型为 DWObject。

5.4.2.2 数据存储对象的事件

数据存储拥有数据窗口的大多数事件,但有些与可视化相关的事件对数据存储是不适

用的。由于数据存储对象中的数据可以被打印输出,所以,PowerBuilder 仍提供了一些与

可视化相关的事件。下面是数据存储的一些主要事件介绍。

(1) DBError:当一个数据错误在数据存储中产生时触发,返回代码为:0 表示显示错

误信息,1 表示不显示错误信息。

(2) ItemChanged:当数据存储调用了 AcceptText()和 Update()函数时触发,返回代码

为:0 表示接受数据(默认),1 表示拒绝数据并且不允许改变焦点,2 表示不拒绝数据但允

许改变焦点。

第 5 章 数据窗口

— 155 —

(3) PrintStart:当开始打印数据存储中的数据时触发,无返回代码。

(4) PrintPage:当数据存储的每一页数据要被格式化用于打印之前触发,返回代码

为:0 表示不跳页,1 表示跳过一页。

(5) updateStart:在调用了 Update()函数之后,并且将被真正地从数据存储修改到数据

库之前时触发,返回代码为:0 表示继续(默认),1 表示不进行修改。

5.4.2.3 数据存储对象的方法和函数

数据存储支持数据窗口控件的大部分方法和函数。下面是一些数据存储的主要函数介

绍。详细使用方法请查阅数据窗口控件的相关部分或 PowerBuilder 的系统帮助。

(1) Create:生成新的数据存储对象。

(2) Deletedcount:获取从数据存储中删除的记录行数。

(3) Deleterow:从数据存储中删除指定行的数据。

(4) GetItem:这是一个系列的函数,可以获取数据存储不同位置的不同类型的数据。

(5) Find:在数据存储的指定范围内查找满足条件的记录。

(6) Print:打印数据存储内的数据。

(7) Reset:清除数据存储的所有数据。

(8) Retrieve:检索数据到数据存储中。

下面以一个用数据存储动态生成一个下拉列表内容的例子,来具体说明如何使用数据

存储。

int j,li_count

//定义一个数据存储变量

DataStore ds_example

//创建数据存储实例

ds_example=Create datastore

//将数据窗口对象赋给数据存储,数据存储的数据就来自指定的数据窗口对象

ds_example.DataObject="d_depart"

//将数据存储对象与事务对象相关联

d_example.SetTransObject(sqlca)

//将数据检索到数据存储中,以供访问,返回的是检索出的记录数

li_count=ds_example.Retrieve()

For j=1 to li_count

//留意对数据存储内数据的引用方式,与使用数据窗口控件是相同的

ddlb_1.additem(ds_example.object.depart_name[j])

Next

//清除数据存储,释放占用的数据存储空间

Destroy ds_example

5.5 数据窗口应用实例

数据存储是 PowerBuilder 提供的不可视数据窗口对象。与数据窗口控件相比,除了

视角属性外其他各个方面都很类似,所以,在此以数据存储为例来说明数据窗口及其数据

存储的使用。

Pow erBuilder9.0 基础应用与系统开发

— 156 —

(1) 首先应用以前建立的学生表 student(sno,sname,sage,sdept)创建一个数据窗口对象

d_student,其显示风格为 Grid,数据源为 SQL Select。数据窗口对象如图 5-55 所示。

图 5-55 数据窗口对象 d_student

(2) 创建窗口 w_student,并向该窗口添加控件 sle_sno,sle_sname,sle_sage,sle_dept,窗

口界面如图 5-56 所示。

图 5-56 窗口结构图

(3) 编写代码。

(4) 在应用程序对象的 Open 事件中编写代码如下。

// Profile Pbdb

SQLCA.DBMS = "ODBC"

SQLCA.AutoCommit = False

SQLCA.DBParm = "Connectstring=’DSN=pbdb’"

connect using sqlca;

if sqlca.sqlcode<0 then

messagebox("提示","连接数据库失败!")

end if

open(w_student)

第 5 章 数据窗口

— 157 —

(5) 在窗口 w_student 中声明一个实例变量。

datastore mydatastore

(6) 在窗口 w_student 的 Open 事件中编写代码如下。

long irc

//定义一个数据存储

mydatastore=create datastore

mydatastore.dataobject="d_student"

mydatastore.settransobject(sqlca)

irc=mydatastore.retrieve()

if irc<=0 then

messagebox("提示","没有检索到数据!")

return

end if

//显示数据

mydatastore.retrieve()

sle_sno.text=string(mydatastore.getitemstring(1,"sno"))

sle_sname.text=string(mydatastore.getitemstring(1,"sname"))

sle_sage.text=string(mydatastore.getitemnumber(1,"sage"))

sle_sdept.text=string(mydatastore.getitemstring(1,"sdept"))

(7) 在命令按钮 cb_1 的 Click 事件中编写代码如下。

//显示上一条信息

long irc,cur_row

cur_row=mydatastore.getrow()

cur_row=cur_row - 1

if cur_row>0 then

mydatastore.setrow(cur_row)

//显示信息

sle_sno.text=string(mydatastore.getitemstring(cur_row,"sno"))

sle_sname.text=string(mydatastore.getitemstring(cur_row,"sname"))

sle_sage.text=string(mydatastore.getitemnumber(cur_row,"sage"))

sle_sdept.text=string(mydatastore.getitemstring(cur_row,"sdept"))

end if

(8) 在命令按钮 cb_2 的 Click 事件中编写代码如下。

//显示下一条信息

long irc,cur_row

cur_row=mydatastore.getrow()

cur_row=cur_row+1

if cur_row>0 then

mydatastore.setrow(cur_row)

//显示信息

sle_sno.text=string(mydatastore.getitemstring(cur_row,"sno"))

sle_sname.text=string(mydatastore.getitemstring(cur_row,"sname"))

sle_sage.text=string(mydatastore.getitemnumber(cur_row,"sage"))

sle_sdept.text=string(mydatastore.getitemstring(cur_row,"sdept"))

end if

Pow erBuilder9.0 基础应用与系统开发

— 158 —

(9) 在命令按钮 cb_3 的 Click 事件中编写代码如下。

close(parent)

至此整个应用程序编写完成,运行程序,其运行界面如图 5-57 所示。

图 5-57 运行效果图

5.6 练习题

(1) 什么是数据窗口对象?什么是数据窗口控件?它们是同一个概念的两种说法吗?

为什么?

(2) 请建立一个依据指定的系和学生的学号检索出相应学生的所有学籍信息的数据窗

口对象,并实现检索操作的界面。

(3) 设计一个实现学生照片编辑的数据窗口对象和编辑照片的界面。

(4) 什么是数据存储?它与数据窗口的区别是什么?请通过数据存储实现第 2 题的检

索功能并通过列表框控件输出检索结果。

(5) 可以在脚本中访问数据窗口中的对象吗?如果可以,请通过一个实例来说明访问

方法。

(6) 如何利用脚本动态创建数据窗口?

(7) 在数据窗口属性框中使用有效性规则和在脚本中使用有效性规则哪个更方便?哪

个更灵活?

(8) 利用数据窗口是否能在异构数据库间传输数据?如果能的话,应该如何实现?

(9) 可以通过哪几种途径将数据窗口中的数据生成为 HTML 文件的格式?

(10) 如何利用数据窗口画板把数据窗口中的数据按 HTML 格式进行存储,同时说明

在脚本中如何调用 Saveas 函数把数据窗口中的数据写到某一 HTML 文件中。

第 6 章 窗口与控件

窗口是应用程序图形界面的基础,它由属性、事件、函数、控件组成,用户通过窗口

及窗口上的控件和菜单表达自己的操作意图。窗口是程序的人机交互界面,良好的用户界

面可以使软件更加受到欢迎。所以如何设计窗口和使用控件是软件设计的重要内容。

6.1 窗口

6.1.1 窗口的类型

使用 PowerBuilder 可以开发出多种流行样式的窗口界面,总共有 6 类窗口可供选

择:主窗口(Main)、多文档窗口(MDI)、带帮助的多文档窗口(MDIHelp)、子窗口(Child)、

弹出式窗口(Popup)和响应式窗口(Response)。

(1) 主窗口,默认情况之下,一个新建的窗口是主窗口,除非选择建立一个多文档应

用。主窗口是独立的窗口,可以带有边框、菜单和工具栏,可以最大化和最小化,是最常

用的一种窗口形式。

(2) 多文档窗口,可以包含子窗口,例如微软的 Word 窗口就是一个典型的多文档窗

口,它也可以具有边框、菜单、工具栏以及最大化和最小化按钮。

(3) 带帮助的多文档窗口,大致与多文档窗口相同。不过它在窗口底部多了一个

MicroHelp 提示栏。

(4) 子窗口,包含在多文档窗口里面,不能超出父窗口的范围,同时也不能拥有菜

单。它可以被最小化,但是最小化后被放到父窗口的底部。

(5) 弹出式窗口,总是浮在父窗口的上面,它的独立性比子窗口要大一些,移动范围

不受父窗口的约束,最小化后,图标出现在屏幕下方。如果父窗口被最小化了,它也会被

最小化,还原时也会连同被恢复。

(6) 响应式窗口,就是接受用户响应的窗口,如果用户不响应,它就不会关闭,

About 窗口就是一个典型的响应式窗口。

6.1.2 创建窗口

可以使用两种方式建立窗口,一种是新建方式,另一种是继承方式。

(1) 新建方式,这个方式比较简单,选择主菜单的 File/New 或直接单击工具栏的

(新建)按钮,选择 Object 标签中的 Window 即可。此时将出现如图 6-1 所示的窗口画

板。画板有 3 个区域,左上方为窗口画板工作区,用于定义窗口及安排控件。左下方为脚

本窗口,用于编写脚本。右方为属性设置区,用于为窗口设置属性。对窗口设计中涉及属

性、事件及函数控件等内容,在后文予以介绍。

Pow erBuilder9.0 基础应用与系统开发

— 160 —

图 6-1 Window(窗口)画板

(2) 继承方式,就是继承原来已有的窗口,方法是选择主菜单的 File/Inherit 或直接单

击工具栏的 (继承)按钮,然后在弹出的窗口中选择一个已有的窗口即可。使用继承方式

来建立窗口需要注意,在后代窗口里不能删除祖先窗口里的控件,不能改变它的名字,只

能修改属性。也不能随便删除祖先窗口的控件,以免后代窗口无法打开。

在建立祖先窗口的时候一定要非常注意,要充分考虑将来可能出现的情况,从而确定

祖先窗口的界面形式和应具备的功能,尽量避免将来修改。万一没有考虑周到,可以在后

代窗口里把不用的控件的 Visible 属性设为 False,或选择不继承某个事件的脚本就行了。

当然,实在没有办法,必须修改祖先窗口时,有可能导致后代窗口无法打开,此时就

需要在库画板里把这个后代窗口导出(Export)来,然后用 Notepad 之类的文本编辑器修改

里面的脚本,然后再导入(Import)到库里面。

6.1.3 窗口的属性设置

Window Printer(窗口画板)的右侧为窗口属性设置栏,如图 6-2 所示。可以在其中进行

窗口属性的设置。通常属性设置后,马上可以在旁边的视图上看到变化。某些属性(例如

ControlMenu)被修改后,视图上并不会立刻显示出来,需要到运行或预览的时候,才可以

反映出来。预览的方法是按工具栏的 按钮,或选择主菜单的 Design/Preview。窗口的属

性分为四大类,以下予以简要介绍。

1. General(常规属性)

(1) 以下文本框中需要输入字符串。

Title:窗口的标题,可输入该窗口的标题。

Tag:窗口的标签,一般用做注释,可以不输入。

MenuName:表示与窗口相连的菜单名字,可以直接输入菜单名称,也可以按旁边的

按钮来选择某个菜单。

第 6 章 窗口与控件

— 161 —

图 6-2 窗口属性设置

(2) 以下属性取决于是否被选中,单击复选框,出现√时该属性被选中,再次单击则

放弃选择。

Visible:选中时表示窗口可见。

Enabled:选中时表示窗口有效。

TitleBar:选中时表示窗口拥有标题栏。

ControlMenu:选中时,表示窗口拥有控制菜单,即单击窗口的图标所弹出菜单。此

项未选中时,标题栏上的最大化、最小化和还原按钮无效。

MaxBox:选中时窗口拥有最大化按钮。

MinBox:选中时窗口拥有最小化按钮。

ClientEdge:选中时显示用户工作区的边框。

PaletteWindow:选中时显示标题栏图标和最大化、最小化按钮(仅用于弹出式窗口)。

ContextHelp:选中时支持上下文帮助。

RightToLeft:选中时菜单、标题、提示都会靠右对齐。

Resizable:选中时表示可以改变窗口的大小。

Border:表示是否显示边框,某些类型的窗口下该属性无效。

(3) 以下属性可以在下拉框中选择。

WindowType:窗口的类型,可以选择前面提过的 6 种类型之一。

WindowState:窗口的状态,可以选择最大化、最小化和正常状态三者之一。

BackColor:选择背景颜色。

MdiClientColor:选择多文档窗口的客户区颜色。

Icon:选择窗口的图标,可以在下拉框中选择,也可以按 选择其他文件。

Pow erBuilder9.0 基础应用与系统开发

— 162 —

2. Scroll(滚动条属性)

Scroll(滚动条属性)设置界面如图 6-3 所示,各属性说明如下。

图 6-3 Scroll 页

HscrollBar:选中时具有水平方向的滚动条。

VscrollBar:选中时具有垂直方向的滚动条。

UnitsPerLine : 当 用 户 单 击 一 下 垂 直 滚 动 条 时 , 窗 口 工 作 区 滚 动 大 小 ( 单 位 是

PowerBuilderUnit)。默认值是 0,表示窗口高度的 1/10。

UnitsPerColumn:当用户单击一下水平滚动条时,窗口工作区滚动的大小(单位是

PowerBuilderUnit)。默认值是 0,表示窗口宽度的 1/10。

ColumnsPerPage:给出每一页显示的列数。默认为 0,表示显示 10 列。

LinesPerPage:给出每一页显示的行数。默认为 0,表示显示 10 行。

3. ToolBar(工具栏属性)

ToolBar(工具栏属性)页如图 6-4 所示,各属性说明如下。

图 6-4 ToolBar 页

ToolbarVisible:选中时工具栏为可见。

ToolbarAlignment:选择对齐方式,可以分别为上、下、左、右对齐或可浮动方式。

ToolbarX,ToolbarY:给出工具栏的左上角坐标。

第 6 章 窗口与控件

— 163 —

ToolbarWidth, ToolbarHeight:给出工具栏的宽度和高度。

4. Other(其他属性)

Other(其他属性)页如图 6-5 所示,各属性说明如下。

图 6-5 Other 页

X,Y:给出窗口的左上角坐标。

Width,Height:给出窗口的宽度和高度。

Pointer:选择窗口的鼠标指针,可以在下拉框选择内置的图标,也可以按 选择其他

图标。

5. Control(控件数组)

Control(控件数组)这个属性是一个数组,它记录了窗口里的所有控件,可以使用它来

引用窗口里的任何一个控件,以下脚本显示如何遍历窗口里的所有控件:

long l_total,j,k

WindowObject wo_t //临时的窗口对象

Tab t_t //临时的标签对象

l_total = upperbound(w_test_01.Control[]) //获得窗口里的控件数量

for j=1 to l_total

wo_t=w_test_01.Control[j]

MessageBox(’ClassName’,wo_t.classname()) //显示控件的名称

if wo_t.typeof()=Tab! Then //如果是标签对象

t_t=wo_t

for k=1 to UpperBound(t_t.Control[])

MessageBox(’Tab’,t_t.Control[k].ClassName() )//显示标签里的对象名

next

end if

next

注意

标签对象是一个容器,里面还可以包含其他的对象,所以其中的对象不直接计入窗口

Pow erBuilder9.0 基础应用与系统开发

— 164 —

的控件,因此查找控件必须象搜索树型结构一样逐层进行,上述脚本只搜索到第 2 层。另

外,如果你正在编辑一个多文档窗口的话,客户工作区也算一个控件。

6.1.4 窗口的事件

PowerBuilder 采用事件驱动的语言,这和 VB 或 Delphi 是相同的。事件就是一个可以

触 发 一 段 脚 本 的 动 作 。 事 件 的 要 素 是 名 称 、 参 数 、 发 生 的 时 机 和 产 生 的 动 作 。

PowerBuilder 提供了丰富的事件,常用的事件有 Open、Close、CloseQuery、Activate、

Key、Clicked 等。合理地使用事件,就可以编写出适当的程序了。想要知道事件发生的

时机,有一个很好的办法,就是打开窗口的事件列表,在该事件的脚本窗口编写

MessageBox(),然后运行程序。例如在 CloseQuery 事件下编写如下脚本:

MessageBox(“注意”,”现在发生了 CloseQuery 事件”)

运行程序,可知在关闭窗口的时候触发 CloseQuery 事件。

图 6-1 中左边下半部分显示的是脚本窗口,上面有 3 个下拉框,左边的是对象名称,

包括了窗口、控件、窗口的函数以及窗口内的变量声明。中间是事件列表,列表上面有个

小图标,如果图标的颜色是纯紫色的,表示该事件的脚本完全继承自祖先对象。如果一半

是紫色的,表示既有祖先对象的脚本,又有后代对象自己的脚本。如果图标没有紫色,表

示该对象没有祖先脚本。右边的下拉框里可以看到当前对象的祖先对象。

本文简要介绍几个窗口事件。

1. Open 事件

程序执行 Open、OpenWithParm、OpenSheet 和 OpenSheetWithParm 函数,窗口被打

开的时候,当所有的控件都已经建立完毕之后发生 Open 事件,这个事件里面通常做一些

修改窗口属性、初始化控件和数据窗口等工作。以下是一段典型的 Open 事件脚本:

cb_Retrieve.Enabled = False //按钮 cb_Retrieve 不可用

dw_dept.SetTransObject(SQLCA) //为数据窗口 dw_dept 设置事务对象

dw_dept.Retrieve() //检索数据

2. Close 和 CloseQuery 事件

用户按下关闭按钮或程序调用关闭函数时,系统逐个撤销(Destruct)控件,之后发生

CloseQuery 事件,然后发生 Close 事件。

CloseQuery 事件里面通常检查用户是否修改了数据窗口的数据,提示用户进行保存

处理。以下一段脚本检查修改和删除的行数,如果发现两者之和大于 0,则提示用户保存

修改。

integer i_answer

if dw_dept.DeletedCount()+dw_dept.ModifiedCount()>0 then

i_answer=MessageBox( ‘ 注 意 ‘ , ‘ 数 据 被 修 改 过 了 , 需 要 保 存 吗 ?

‘,Question!,YesNoCancel!)

choose case i_answer

case 1 //如果选择了 Yes,保存数据

第 6 章 窗口与控件

— 165 —

dw_dept.Updtae()

return 0

case 2 //如果选择了 No,不保存,退出

return 0

case 3 //选择了 Cancel,不保存,不退出

return 1

end choose

end if

return 0

3. Timer 事件

每隔一段固定时间发生的事件可以作为 Timer 事件来编写。要使用 Timer 事件,先要

调用一个函数,其调用格式为:

Timer ( interval {, windowname } )

其中 interval 是 Timer 事件发生的间隔,以秒为单位,可以是小数,有效范围是 0 到

4294967,如果间隔是零的话,Timer 事件不再发生。注意,虽然可以使用小数,但是结

果并不一定按照你所指定的间隔执行,因为这是由操作系统决定的。在 win9x 下,

interval 的最小精度是 50ms 左右,并且不一定准确,而在 Windows NT 下可以达到 10ms

的精度。

windowname 是所指定的一个窗口的名字,这个窗口必须是已经打开的。如果不指定

windowname,则默认为当前的窗口。

以下的例子可以显示一个霓虹灯式的窗口标题。首先在 open 窗口事件激活 timer 事

件:

timer(0.2)

然后为窗口声明两个局部变量:

string s_Title=’Hello’

integer i=1

最后在 timer 事件里编写如下脚本:

this.Title=Fill(’ ’, i ) + mid(s_title,i,1)

i++

if i=6 then i=1

return 0

4. Resize 事件

顾名思义,窗口改变大小的时候发生 Resize 事件。不过在窗口被打开的时候,也会

发生 Resize 事件。通常在这个事件里调整控件的位置和工作区的大小,以适应新的窗口

的尺寸。

5. Other 事件

PowerBuilder 已经把很多常用的事件提供给开发人员了,但是还是有一些事件没有提

Pow erBuilder9.0 基础应用与系统开发

— 166 —

供,这些事件都包含在 Other 事件里面。Other 事件的原型是:

Other(unsignedlong wparam, long lparam)

除了已知的事件,其他所有的事件消息都会发送给 Other 事件来处理,所以除非开发

人员十分熟悉操作系统的消息机制,一般不使用 Other 事件。

6. 用户自定义事件

用户可以在脚本窗口中自己定义事件,事件的名称和事件发生的时机都可以由用户自

己确定,同时,用户事件可以被后代继承。用户自定义事件的方法是,在脚本窗口的事件

下拉框里选择最上面的 New Event,输入事件的名称、参数和返回值类型等数据,然后在

脚本区输入这个事件的响应脚本,如图 6-6 所示。

图 6-6 脚本窗口

事件定义好之后,还有一件重要的事情就是触发这个事件,这要使用到 TriggerEvent()

函数。假设准备在一个按钮被按下的时候触发这个事件,可以在按钮的 Clicked 事件中加

入脚本:

Parent.TriggerEvent( �TicTac� )

6.1.5 窗口的函数

PowerBuilder 为窗口提供了 40 多个函数,其中有些很少用到,这里只介绍部分常用

的函数。

Close:关闭窗口。

CloseWithReturn:关闭窗口,并且返回一个值,该值保存在全局变量 Message 中。

GetActiveSheet:在多文档窗口下获得当前的工作表。

GetFirstSheet:在多文档窗口下获得第 1 个工作表。

GetNextSheet:在多文档窗口下获得下一个工作表。

Hide:隐藏窗口。

Move(x,y):移动窗口的位置,(x,y)是其左上角新的坐标。

第 6 章 窗口与控件

— 167 —

Print:打印窗口。

Resize(width,height):改变窗口的大小,width 为新的宽度,height 为新的高度。

SetFocus:使窗口获得焦点。

SetMicroHelp(helpstring):设置窗口的微帮助(MicroHelp)。

SetRedraw(boolvalue):设置自动重画属性。

Show:显示窗口。

TriggerEvent(eventname):触发一个事件,在 eventname 中指定事件名称。

6.1.6 在窗口之间传递参数

PowerBuilder 提供了 OpenWithParm 函数、CloseWithParm 函数和 Message 这个全局

变量来实现窗口之间的参数传递。在打开窗口的时候调用 OpenWithParm 把参数存储在

Message 里,被打开的窗口则在 Open 事件读取 Message 里的参数,处理完毕之后,在

Close 事件里使用 CloseWithReturn 把参数返回调用的窗口。Message 中用来存储参数的 3

个属性如下。

(1) Message.StringParm 记录字符串参数。

(2) Message.DoubleParm 记录数字型参数(双精度)。

(3) Message.PowerObjectParm 记录 PowerObject 型的参数。

OpenWithParm 和 CloseWithParm 的原型是:

OpenWithParm ( windowvar, parameter {, parent } )

CloseWithReturn ( windowname, returnvalue )

如果要传递多个参数,可以把它们存储在一个结构里面,然后使用 PowerObjectParm

来传递参数即可。下面的例子在一个窗口里打开 w_win2open 窗口,传入一个字符串参

数 , 然 后 w_win2open 返 回 一 个 数 字 给 调 用 的 窗 口 。 在 调 用 窗 口 的 时 候 使 用

OpenWithParm 传入参数,然后从 Message.DoubleParm 读取返回值,脚本如下:

OpenWithParm(w_win2open,’ParmString’) //打开窗口 w_win2open,传入字符串

ParmString

If Message.DoubleParm>0 then // Message.DoubleParm 中存放返回值

. . .

else

. . .

end if

注意

一般来说,被调用的窗口都是一个响应式的窗口,所以 OpenWithParm 以后的脚本都

是在它被响应之后才被调用的。

在 w_win2open 的 open 事件里使用如下脚本读取传入的参数。

string s_parm

s_parm=Message.StringParm //将传入参数值赋给字符串变量 s_parm

Pow erBuilder9.0 基础应用与系统开发

— 168 —

if s_parm = �ParmString� then . . .

在 w_win2open 的 Close 事件调用如下脚本:

real r_return_value . . . CloseWithReturn(this,r_return_value) // r_return_value 中存放返回值

6.2 控件

6.2.1 概述

建立了窗口之后,就可以往里面放置控件了。控件是构成 PowerBuilder 应用程序用

户界面,完成数据输入、输出的强有力工具。利用控件,能完成许多界面设计任务而无需

编码。所谓控件,实际上就是系统预先定义好的图形对象,开发人员通过把这些对象添加

到窗口上而直接使用它们。PowerBuilder 提供了 30 多种控件供开发人员选用。表 6-1 列

出了各控件的名称、在窗口画板工具栏上的图标及其主要用途。使用这些控件可以基本满

足开发应用软件的需要,此外也可以调用 OLE 控件和建造用户对象来进行扩展。

表 6-1 控件概览

控 件 中文名 图 标 用 途 CommandButton 命令按钮 激发一特定动作

PictureButton 图像按钮 激发一特定动作,按钮上有图像

CheckBox 复选框 对彼此独立的选项进行设置

RadioButton 单选按钮 对一选项的选与不选进行设置

StaticText 静态文本 用于标题或提示信息

StaticHyperLink 静态超链接控件 连接到指定网址上

Picture 图像控件 用来显示图像

PictureHyperLink 图像超链接控件 连接到指定网址上

GroupBox 分组框 将相关的控件分组

Line 直线 画直线

Oval 椭圆 画椭圆

Rectangle 矩形 画矩形

RoundRectangle 圆角矩形 画圆角矩形

SingleLineEdit 单行编辑框 对单行文本进行编辑

EditMask 掩码编辑框 对符合一定格式的文本进行编辑

MultiLineEdit 多行编辑框 对多行文本进行编辑

RichTextEdit 丰富文本编辑框 对不同格式的文本进行编辑

HScrollBar 水平滚动条 为用户指示在一连续尺度上的值

VScrollBar 垂直滚动条 为用户指示在一连续尺度上的值

HTracklBar 水平轨迹条 带刻度的水平滚动条,移动时不连续

VTrackBar 垂直轨迹条 带刻度的垂直滚动条,移动时不连续

HProgressBar 水平进度条 指示操作的进度,控件水平放置

VProgressBar 垂直进度条 指示操作的进度,控件垂直放置

DropDownListBox 下拉列表框 编辑或显示一系列选项或数值

DropDownPictureListBox 图像下拉列表框 编辑或显示一系列包含图像的选项或数值

第 6 章 窗口与控件

— 169 —

(续表)

控 件 中文名 图 标 用 途 ListBox 列表框 显示一系列选项或数值

PictureListBox 图像列表框 显示一系列包含图像的选项或数值

ListView 列表视图控件 以列表的方式显示信息

TreeView 树型视图控件 以树形的方式显示信息

Tab 标签控件 以分页的形式显示信息

DataWindow 数据窗口控件 显示或操纵数据

Graph 统计图 以图形化的形式来显示数据

OLE OLE 控件 嵌入或链接 OLE 客户程序

User Object 用户对象 用户自定义对象

6.2.1.1 工具栏

有关控件的工具栏如图 6-7 所示,图中解释了各工具的作用。利用这些工具可以对控

件进行相应的操作。

图 6-7 控件的工具栏

6.2.1.2 在窗口上放置控件

按以下步骤在窗口上放置控件。

(1) 选择菜单 Insert|Control,PowerBuilder 会在 Control 的右侧弹出控件列表菜单,在

此列表中选择要放置的控件。

(2) 在用户工作区的适当位置单击,将控件放置此窗口上。

也可按以下步骤进行。

(1) 单击工具栏上的 按钮,在下拉框中选择控件。

(2) 在用户工作区的适当位置单击,可以看到单击的位置上放置了你要放置的控件。

6.2.1.3 设置属性和编写脚本

控件的属性在窗口画板的属性窗口设置。属性放在多个标签页下设置和显示,图 6-1

右侧给出 General 标签页显示的内容,在其中可以对控件的名称、文本和注释等属性进行

设置。另一些属性分别在 Font 及 Other 标签页中设置。

双击控件,脚本窗口就会自动显示该控件的相应脚本,如图 6-8 所示。通常一个控件

都会有多个事件,图中所示为命令按钮 cb_OK 的 clicked 事件的脚本。脚本窗口中 cb_ok

所在的下拉列表框为对象列表框,它右边的下拉列表框为事件列表框。事件列表框的右边

还有一个下拉列表框,显示的是该控件的祖先对象,假如窗口继承自一个祖先窗口,会自

动显示出来。

Pow erBuilder9.0 基础应用与系统开发

— 170 —

图 6-8 控件的属性设置和脚本编写

脚本窗口上方最右边还有两个切换按钮,均可以在按下和弹起两种状态之间切换,按

下左边的按钮时可显示事件脚本或函数脚本的原型,按下右边的按钮时可以显示错误消

息。

6.2.1.4 焦点与 Tab 顺序

焦点反映控件是否具备接收用户鼠标或键盘输入的能力。当控件具有焦点时,才能接

收用户的输入。当对象得到焦点时,它将产生 GotFocus 事件;当对象失去焦点时,它将

产生 LostFocus 事件。程序运行时,从窗口控件的外观可以看出它是否得到焦点。例如命

令按钮具有焦点时,按钮周围的边框内就出现一个虚框。每一个控件都有一个 Tab 顺序号

码(Tab Order),所谓 Tab Order 就是程序运行过程中,用户按下 Tab 键时,控件焦点跳动

的顺序。设置 Tab Order 的步骤如下,按下 Tab Order 按钮(图 6-9 中椭圆里的按钮),按钮

呈下陷的状态,控件的 Tab Order 就会以红色的数字在控件右上方显示出来。第 1 个获得

焦点的控件的 Tab Order 值是 10,其他控件的 Tab Order 都是 10 的倍数。单击控件的 Tab

Order 值,在里面输入一个新的数值就是这个控件的 Tab Order,输入值的范围是 0-999。

新的 Tab Order 值可以不是 10 的倍数,但是下一次修改它的时候,PowerBuilder 会根据它

们的大小重新排列成 10 的倍数。这样可以方便开发人员重新插入一个新的 Tab Order。

图 6-9 设置 Tab Order

第 6 章 窗口与控件

— 171 —

修改完毕之后,再次单击 Tab Order 按钮,这时它就恢复到没有被按下的状态了,至

此 Tab Order 的设置完成了。

6.2.1.5 控件名称的前缀

可以在主菜单的 Design 项下的 Options 里面 Prefixes 1 和 Prefixes 2 设置控件名称的

前缀,图 6-10 给出 Prefixes1 页的内容。默认的前缀使用控件名称的英文名的缩写,建议

开发人员使用默认的设置,这样就可以使其他开发人员能够比较容易地读懂程序了。

图 6-10 控件名称前缀的设置

6.2.2 CommandButton(命令按钮)和 PictureButton(图像按钮)

6.2.2.1 CommandButton(命令按钮)

CommandButton(命令按钮)是最常用的控件,其作用是接受用户的单击,触发一段处

理。命令按钮比较简单,表 6-2 给出了一组命令按钮的实例。

表 6-2 命令按钮实例

属性

种类 Name Text Enabled Default Cancel 按钮样式

默认按钮 Cb_OK 确定(&O) True True False

失效按钮 Cb_Print 打印(&P) False False False

普通按钮 Cb_Preview 预览(&V) True False False

取消按钮 Cb_Cancel 取消(&C True False True

Pow erBuilder9.0 基础应用与系统开发

— 172 —

其中 Cb_OK 是默认按钮,在这个窗口下按 Enter 键,等于默认单击了该按钮;而

Cb_Cancel 是取消按钮,在这个窗口下按 Esc 键,就等于默认单击了该按钮。

也可以在程序中设置这些属性,例如要使 cb_Print 可用,可以这样写:

cb_Print.Enabled=True

命令按钮的最常用事件是 Clicked 事件,还有 GetFocus 和 LostFocus 事件也可能会用

到。

6.2.2.2 PictureButton(图像按钮)

PictureButton(图像按钮)是命令按钮的扩展,可以在按钮的表面增加图像。它的函数

和事件跟命令按钮相同,属性比命令按钮多了几个,这里只介绍扩展的属性。

(1) PictureName 指定按钮表面的图像。

(2) DisabledName 指定按钮失效时的图像。

(3) HtextAlign 选择按钮上的文本在水平方向上的对齐方式。

(4) VTextAlign 选择按钮上的文本在垂直方向上的对齐方式。

(5) OriginalSize 选择是否使用图像的原始大小。

6.2.3 RadioButton(单选按钮)和 CheckBox(复选框)

6.2.3.1 RadioButton(单选按钮)

RadioButton( 单 选 按 钮 ) 用 于 在 一 组 选 项 中 进 行 单 项 选 择 , 一 般 先 建 立 一 个

GroupBox(组框),然后再往里面放单选按钮,运行的时候读取 Checked 属性,就可以知道

用户选择了哪一个选项。可以在属性窗口里预先设置选项按钮的 Checked 属性。

属于同一个组框的选项按钮的 Checked 属性有互斥的特性。也就是说,当某一个选项

按钮被选中的时候,组中其他的所有选项按钮的 Checked 属性都会自动变成 False。

图 6-11 的左边给出一组单选按钮的实例。

6.2.3.2 CheckBox(复选框)

CheckBox(复选框)用于表示是/否状态的输入,

在 PowerBuilder 的属性窗口里就有大量的属性是使

用复选框的。图 6-11 右侧是一组复选框。

复选框的主要属性是 Checked,当方框被打上钩

的时候,Checked 为 True,没有的时候为 False。

复选框一共可以有选中、未选中和不确定这 3 种状态,PowerBuilder 把不确定状态称

为第 3 态。默认情况下一个复选框只有两个状态,在属性窗口里选中 ThreeState 即可使用

第 3 态,当复选框处于第 3 态时其 ThirdState 属性为 True。注意,此时 Checked 属性仍然

是 True。

单选按钮和复选框都具有一个 Automatic 属性,默认情况下,这个属性等于 True,这

表示用户可以通过单击修改 Checked 属性。如果把它设为 False,在程序运行的时候,用

图 6-11 单选按钮和复选框

第 6 章 窗口与控件

— 173 —

户单击单选按钮和复选框时不能修改 Checked 属性,而仅仅把设置显示出来,这可以用来

防止用户修改一些设置,当然使用脚本还是可以修改 Checked 属性的。

6.2.4 Static Text(静态文本)

Static Text(静态文本)一般只作显示文本使用,运行期间可以用脚本修改文本,一般不

对其事件编写脚本。静态文本重要的属性如下。

(1) Text:用于显示的文本。

(2) Disabled:如果选中,静态文本的外观不会改变,只是事件不再有效。

(3) DisabledLook:选中时显示成失效的灰色状态,注意,该属性与 Disabled 属性无

关。

(4) FocusRectangle:选中时表示获得焦点的时候是显示矩形边框。

(5) Border:选中时表示使用边框。

(6) BorderStyle:选择边框的风格。

此外,还有诸如文本字体等属性可以调整,开发人员可以自由地选择适当的属性来达

到期望的效果。

6.2.5 SingleLineEdit(单行编辑框)

SingleLineEdit(单行编辑框)是最常用的控件之一,用来输入各种数据。其主要属性如

下。

(1) DisplayOnly:选中时表示只作显示使用,用户将不能修改编辑框里的内容;

(2) Password:选中时,输入的字符都会以星号显示,可用于输入密码。

(3) HideSelection:若选中此项,程序运行的时候,用户高亮选定的文本,在控件失

去焦点的时候,仍然保持高亮。此项默认为 True,即失去焦点时,仍保持高亮。

(4) TextCase 该属性可以选择编辑框的字母是大写、小写或任意。

(5) Limit:表示编辑框里最多可以输入多少个字符,范围是 0~32767,0 表示没有限

制。

(6) Accelerator:加速器,在这个属性里可以输入一个字母,例如 a,表示用户按下

Alt+A 就可以快速地到达编辑框。

单行编辑框最重要的事件是 Modified,发生在输入完毕,用户移走焦点之后。可以在

Modified 事件里对输入进行有效性检验,下面的脚本是一个例子。

if NOT isNumber(this.text) then this.SetFocus()

如果输入的不是一个数字,焦点重新回到编辑框。

6.2.6 EditMask(掩码编辑框)

EditMask(掩码编辑框)用于输入具有一定格式的文本或数据,例如日期和一些票据的

编号等。可以创建多种掩码,强制用户按照掩码格式输入数据。PowerBuilder 总共支持 4

种格式的掩码,可以在图 6-12 所示的属性窗口 Mask 标签页下设置 MaskDataType,他们

是数字、字符串、日期和时间。

Pow erBuilder9.0 基础应用与系统开发

— 174 —

图 6-12 掩码属性的设置

也可以直接输入自己定义的掩码格式,表 6-3 列出掩码格式中的字符意义。

表 6-3 掩码格式

类 型 格式符 说 明 例 子

# 表示一个数字(可以使用多次) (略)数字

0 表示一个数字,如果没有输入数字的话,会以一个 0 表示(可以使用多次) (略)

字符串 a 表示任意一个字符(可以使用多次) (略) d 日期中的“日”,不带前面的 0 9

dd 日期中的“日”,不足两位的话前面加上一个 0 09

ddd 表示星期几,缩写 Mon

dddd 表示星期几,全称 Monday

m 表示月份,不带前面的 0 7

mm 日期中的月份,不足两位的话前面加上一个 0 07

mmm 月份的英文缩写 Jun

mmmm 月份的英文全称 August

yy 两位数字的年份 99

日期

yyyy 四位数字的年份 2001

h 小时,前面没有 0 9, 12

hh 小时,如果不足两位数,前面补一个 0 09

m 分钟,前面没有 0 8,45

mm 分钟,如果不足两位数,前面补一个 0 08,45

s 秒,前面没有 0 8

ss 秒,如果不足两位数,前面补一个 0 09

f 微秒数,可以使用 1~6 个 f 来表示 (略)

AM/PM 用 AM 和 PM 表示上午和下午 AM

am/pm 用 am 和 pm 表示上午和下午 pm

A/P 用 A 和 P 表示上午和下午 P

时间

a/p 用 a 和 p 表示上午和下午 a

上面的格式符号可以组合使用。例如需要输入由 1 个字母和后接 6 个数字组成的编

号,可以用 a######作为掩码。

掩码编辑框还允许以自动增量的方式进行输入,此时编辑框的右边就会有一个上下箭

头,单击箭头就可以增量输入。设置的方法是:选中 Spin 属性,设置步长 Increment 和最

第 6 章 窗口与控件

— 175 —

大值(Max)、最小值(Min)。

增 量 方 式 下 还 可 以 使 用 一 种 更 加 灵 活 的 形 式 , 就 是 使 用 自 定 义 代 码 表

(UserCodeTable),在这个表格里输入自己的代码表,其中 Display Value 是显示出来的值,

Data Value 是实际记录的数据。可以使用 GetData 函数来获得实际数据。假设实际数值是

字符串型的,可以用如下脚本获得:

string s_value

em_1.GetData(s_value)

6.2.7 ListBox(列表框)和 PictureListBox(图像列表框)

ListBox(列表框)和 PictureListBox(图像列表框)以列表的方式向用户显示信息。列表框

使用文字的形式,而图像列表框则可以用图像方式。图 6-13 的左图显示的是列表框的

Items 属性,中间和右边的两幅图是图像列表框的属性。可以看到图像列表框只比列表框

多了一个图像而已。

Items 的内容就是列表框或图像列表框中显示的内容,可以在属性窗口的 Items 页预

先输入(参见图 6-13 的左图和右图)。对于图像列表框的 Items 页还有一个 PictureIndex

项,指的是 Pictures 页上的序号。在 Pictures 页中要输入图像名、图像的宽度及高度,此

外,选项卡里有一个 PictureMaskColor(图像掩模颜色),选择一个颜色,在显示图像的时

候,图像中与掩模颜色相同的像素都会被显示成透明。

图 6-13 列表框和图像列表框的属性设置

PowerBuilder 提供了一些内置的图标供开发人员使用,当然这肯定不够,可以单击图

像旁边的 按钮选择一个其他的图像文件。

列表框和图像列表框的常用事件是 SelectionChanged 和 DoubleClick,以下例子可以

放在这两个事件里。

MessageBox(‘你所点中的内容是’,This.SelectedItem() )

可以使用 SelectedIndex 和 SelectedItem 来获得被选中的项,SelectedIndex 表示被选中

项的索引值(从 1 开始计算),而 SelectedItem 表示它的文本。

列表框和图像列表框支持多项选择,这时需要选中 MultiSelect 属性。还可以指定

Pow erBuilder9.0 基础应用与系统开发

— 176 —

ExtendedSelect 属性,它表示用户可以使用 Ctrl 或 Shift 加上鼠标单击来多选。在程序运行

的时候,可以使用 SelectedIndex 来获得被选中的项,然后使用 State 函数来获得每一个项

的状态,其原型为:

listboxname.State ( index )

如果 index 所指定的项被选中,State 函数将返回 1,否则返回 0,而如果 index 不合

法,State 函数返回 NULL。

下面给出创建和使用一个 PictureListBox 控件和编程的示例。

(1) 在窗口里布置三个控件:一个命令按钮(cb_Show)、一个图像列表框(plb_1)、一个

静态文本(st_1)。其中 plb_1 的 VscrollBar、MultiSelect 和 ExtendedSelect 设置为 True。

(2) 在窗口的 Open 事件里编写如下脚本:

plb_1.DirList(’c:\*.*’,0)

这表示在图像列表框里显示 c:\下的所有文件,0 表明文件的类型是可读写的。

(3) 在 plb_1 的 SelectionChanged 事件加入如下脚本:

st_1.text=this.SelectedItem()

这表示每当选中的项有所改变的时候,马上在下面的静态文本框里显示新的文件名

称。

(4) 在 cb_Show 的 Clicked 事件里加入如下脚本:

integer I

string s_selected

for i=plb_1.SelectedIndex() to plb_1.TotalItems()//逐个记录被选中的文件名

if plb_1.State(i) =1 then s_selected = s_selected + plb_1.text(i) + ’~n’

next

if s_selected<>’’ then // 用消息框显示出来

MessageBox(’被选中的项有:’,s_selected)

else

MessageBox(’遗憾’,’没有任何项被选中!’)

end if

程序的意图在程序中已给出,其运行结果如图 6-14 所示。

图 6-14 图像列表框的示例

第 6 章 窗口与控件

— 177 —

6.2.8 ListView(列表视图)

列表视图使用列表、图标、文字视图的方式来显示数据。视图里可以放置图标、文

本,用表格的形式来显示。Windows 的资源管理器就是一个列表视图。视图的显示形式

可以在属性窗口的 View 下拉框里选择。

(1) listviewlargeicon:大图标。

(2) listviewsmallicon:小图标。

(3) listviewlist:列表。

(4) listviewreport:报表。

对于使用图标的视图,需要在属性窗口中指定使用的图标。对于大图标,在

LargePicture 页设定;对于小图标,在 SmallPicture 也设定。可以预先设定多个图标,然

后在属性设置框的 Items 页里输入相关内容,其中 PictureIndex 使用在图像页里的序号。

列表视图的主要属性如下。

(1) FixedLocation 选中此项时,图标的位置是固定的。

(2) EditLabel :若选中,在文本上单击两下(不是双击)可以修改标签文本。

(3) AutoArrange:自动安排图标。

(4) ExtendedSelect:选中时,允许用 Ctrl,Shift 或拖动画框的方式多选。

(5) ButtonHeader:选中时使用按钮的形式显示标题。

(6) DeleteItems:选中时可以用 Del 键在视图里删除一个项。

(7) HideSelection 此项被选中时,控件失去焦点后,高亮选中的项是否同时失去高

亮状态。

(8) CheckBoxes 选中时,视图的每一个项旁边加上一个复选框。

(9) TrackSelect:此项被选中时,当鼠标在一项经过时,这一项会改变颜色,如果鼠

标在一项停留下来,这一项会自动被选中。

(10) OneClickActivate:选中时表示由单击触发 Activate 事件。

(11) TwoClickActivate:选中时表示由两个单击触发 Activate 事件。

(12) GridLines:选中时,显示表格线条(仅用于 Report 视图)。

(13) HeaderDragDrop:选中时标题可被拖动。

(14) FullRowSelect:选中时可以选择整行(仅用于 Report 视图)。

下面给出一段列表视图的程序示例,图 6-15 是一个大图标视图显示的结果,其中显

示的是数据库中的专业信息。

ds_zy=Create DataStore //建立一个数据存储

ds_zy.DataObject="d_zy" //为数据存储指定一个数据窗口,d_zy 里有两个字段,zydm 和

zymc

ds_zy.SetTransObject(SQLCA) //指定事务对象

ds_zy.Retrieve() //检索数据

long i

ListViewItem l_lvi

for i=1 to ds_zy.RowCount()

Pow erBuilder9.0 基础应用与系统开发

— 178 —

l_lvi.PictureIndex = 1 //指定一幅图片

l_lvi.Label = ds_zy.object.zymc[i] //使用专业名称作为文本标签

l_lvi.Data = ds_zy.Object.Zydm[i] //实际数据保存的是专业代码

lv_1.AddItem(l_lvi) //向列表视图里增加一个项

next

图 6-15 列表视图的示例

关于数据窗口和数据存储,请参阅有关章节。细心的读者可能发现视图里的图标不是

透明的,如果要把图标做成透明的,必须在 LargePicture 属性里把 LargePictureMaskColor

设置为图标的底色。同理,如果使用的是 SmallPicture,则在相应的地方作类似的修改。

6.2.9 下拉列表框和图像下拉列表框

下拉列表框使用一种比列表框更加紧凑的显示方式。它有一个文本框和一个列表框。

平时只显示一个文本框,选择的时候列表框被拉下来。默认情况下,文本框里的内容只能

是列表框里的项。如果选中 AllowEdit 属性,则可以在文本编辑框输入其他的内容。

默认情况下,列表框会自动隐藏,如果选中 ShowList 属性,则可以使列表框总是显

示出来。如图 6-16 所显示的两个下拉列表框中,左边的下拉列表框的 ShowList 被选中,

右边的则没有选中。

图像下拉列表框与下拉列表框类似,只是每一个项的左边可以多一个图像。图像可以

在属性窗口的 Pictures 标签页里输入,同时 Items 标签页里需要指定 Pictures 的索引号。

列表框里的内容可以在属性窗口的 Items 标签页里输入。也可以在运行的时候用脚本

动态地生成。相关的函数如下。

(1) AddItem()用于向下拉列表框添加一项。

(2) InsertItem()在某个位置插入一项。

(3) DeleteItem()删除一项。

(4) FindItem()在下拉列表框的内容中查找一项。

(5) Reset()删除所有的项。

第 6 章 窗口与控件

— 179 —

图 6-16 下拉列表框的属性设置

与列表框相似,可以使用 DirList()显示某个文件夹的文件名称,不过 PowerBuilder 推

荐使用 GetFileOpenName 和 GetFileSaveName 选择文件名,所以实际上 DirList 并不常用。

注意

下拉列表框不提供类似 SelectedIndex 的函数,所以没有一个直接的办法来获得当前

被选中的项的索引值,只能用 Text 属性来获得文本。一个解决办法就是在窗口设置一个

实例变量,在下拉列表框的 SelectionChanged 事件发生的时候,用这个变量记录 index,

然后在需要的地方引用这个变量即可。

6.2.10 Tab(标签控件)

6.2.10.1 概述

如果界面里的控件太多,不方便布置的话,可以用标签把它们分别放在若干个页里

面。每一个标签好像一个容器,可以放置多个页,而页也可看作一个容器,里面可以容纳

多个控件。要设置标签的格式,就要选中标签控件,方法是选中标签控件的标题区域,如

图 6-17 左图的椭圆所指的区域。如果要设置页的属性,就要选中主体(标题以外的部分)。

在标签控件的顶部单击鼠标右键,在弹出的菜单里面选择 Insert TabPage,在当前标

签控件下就可增加一页并可以在属性窗口中为该页命名。

一个标签可以拥有多个页,一个页又可以包含多个控件。同一个标签下的各个页使用

相同的字体,但是各个页可以有不同的颜色和背景色。

6.2.10.2 调整页的顺序

页的顺序调整可以在标签属性窗口的 PageOrder 页(如图 6-17 的右图)进行,方法是选

中一行,拖动到新的位置。如果需要在运行的时候动态改变页的顺序,可以使用

MoveTab( )函数,格式是:

Pow erBuilder9.0 基础应用与系统开发

— 180 —

TabName.MoveTab (source, destination )

图 6-17 标签的设计

该函数表示把第 source 页移动到 destination 页之前。Source 和 destination 都是整数

型,从 0 开始计数。如果 destination 大于总的页数,则新的位置就是最后的一页。注意,

虽然 MoveTab( )函数可以移动页的位置,但是其内容不会马上更新,还需要用户单击选中

一页才能刷新。当然,最好的办法是使用脚本自动刷新,方法如下:

Tab_1.MoveTab(1,6)

Tab_1.SelectTab(5)

这表示把第 1 页移动到第 6 页之前,然后把它选中。

6.2.10.3 设置标题的显示格式

有多种方式显示标签的标题,可以横向、纵向,标题也可以放在主体的上、下、左、

右位置。在标签的属性窗口选中 Perpendicular Text 项可以把标题文本设置为纵向显示,

如图 6-18 的左边嵌套的标签所示。如果页太多,这时需要选中 Multiline 项,以便使用多

行的标题。如图 6-18 右侧图所示。

图 6-18 各种形式的标签

标题上可以显示图像,设置的方法是,首先选中标签的 ShowPicture 属性,然后在具

体的页的属性窗口的 TabPage 页下有个 PictureName 属性,选择预定义的图标或其他图片

文件,此时无论图像是多大,只能以图标的形式显示。图标的默认位置在文本的左边,也

第 6 章 窗口与控件

— 181 —

可以通过选中 PictureOnRight 或用脚本来使图标显示在文本的右边。

标签的属性中有个 BoldSelectedText,可以用粗体显示选中的页的标题。

6.2.11 统计图

6.2.11.1 概述

PowerBuilder 提供了丰富的统计图显示方式,其中包括:平面、3D、折线型、柱

型、饼型、离散型等总共 17 种。统计图由以下几部分组成。

(1) Title(标题):整个统计图的标题。

(2) Axis(坐标轴):Value(纵坐标为数值),Category(横坐标为类别),Series(纵深方向的

轴称为序列)。

(3) Series(序列):就是一系列的数值,设计统计图的时候,PowerBuilder 提供 3 个序

列供开发人员预览效果,分别是 Mac、Unix、Windows,每一个序列都有一系列的数值。

它们分别对应不同的计算机系统,如图 6-19 所示。

图 6-19 统计图控件

(4) Legend(图例):显示各个序列的名称和颜色,PowerBuilder 自动安排各个序列的颜

色。

6.2.11.2 设置显示格式

在属性窗口的 General 标签页设置统计图的基本属性,对一些重要的属性说明如下。

(1) GraphType:统计图的类型,共有 17 种供选择。

(2) SeriesSort:序列名称的排序(升序、降序、不排序)。

(3) CategorySort:横坐标内容的排序。

(4) Legeng:图示的位置(上、下、左、右、无)。

(5) Perspective:视点距离(仅用于 3D 图)。

(6) Elevation:仰角(仅用于 3D 图)。

(7) Rotation:旋转角度(仅用于 3D 图)。

(8) Spacing 相邻数据条之间的空白量(见图 6-20)。

(9) Depth:数据条的深度(仅用于 3D 图)。

(10) OverLapPercent:不同数据条重叠部分占数据条宽度的比例(见图 6-20)。

Pow erBuilder9.0 基础应用与系统开发

— 182 —

OverLapPercent

数据条

Spacing

图 6-20 统计图的属性

坐标轴的属性在 Axis 页进行设置。对于平面的统计图,总共有两个轴――Value 和

Category,对于 3D 的统计图,会自动增加一个 Series 属性。

选择了 Axis 以后,就可以对这个坐标轴的属性进行设置了。有如下属性可以设置。

(1) Label:坐标轴的标题。

(2) ShadeBackEdge:坐标所在平面是否有阴影,阴影颜色可以在整个控件里设置,该

属性仅用于 3D 图。

(3) AutoScale:自动对标尺进行调整,仅用于 Value 坐标轴。

(4) DataType 指出要将数值的类型转换成何种类型。

(5) RoundTo 给出当设置了 AutoScale 时,Value 坐标标尺的一个刻度所代表的数

值。

(6) RoundToUnit:当设置了 AutoScale 的时候,Value 坐标的数值的单位。

(7) MinimumValue:Value 坐标的最小值。

(8) MaximumValue:Value 坐标的最大值。

(9) DisplayEveryNLabels:表示在坐标轴上隔多少个刻度显示一个数值。

另外,统计图中各种文本的字体和颜色在属性窗口的 Text 页设置。做法是在

TextObject 下拉框里选择标题、图例或坐标轴,然后在下面选择相应的字体设置即可。

6.2.11.3 输入数据

不能在设计控件时输入数据,必须在脚本中利用函数把数据传递给统计图控件。这里

要使用到以下几个函数。

(1) graphname.AddSeries(seriesname)用于增加一个序列,其中 seriesname 是字符串型

的序列名称。

(2) graphname.AddCategory(categoryname)用于增加一个类别,categoryname 是字符串

型的类别名称。

(3) graphname.AddData(seriesnumber,datavalue{,categoryvalue})用于增加一个数据,其

中 seriesnumber 是序列的编号,可以使用 FindSeries 函数获得;datavalue 是一个数值;

categoryvalue 是字符串类型的,这是一个可选项,如果脚本中不指定,PowerBuilder 将自

动按照顺序给数值编排一个类别号。

(4) controlname.FindSeries(seriesname)用于查找一个序列的编号,其中 seriesname 给出

序列的名称。

第 6 章 窗口与控件

— 183 —

下面给出一个简单的示例,实现向统计图控件添加随机的数据,设置不同的显示方

式,进行三维旋转、视角设置等功能。

先按图 6-21 所示在窗口中放置控件,其中左边为统计图控件 gr-1,窗口的右方和下

方依图添加两个下拉框列表框(命名为 ddlb_Series,ddlb_GraphType)、一个单行编辑框(命

名为 sle_Series)、两个命令按钮(命名为 cb_AddSeries,cb_AddData)、三个水平轨迹条(命

名为 htb_Perspective,htb_Rotation,htb_Elevation)。向 ddlb_GraphType 的 Item 属性里增

加 17 种统计图类型的名称,htb_Rotation 和 htb_Elevation 的最小值设为-90,最大值设为

90。

图 6-21 统计图的示例

以下是对各个控件编写的脚本。

(1) htb_Perspective 的 moved 事件脚本。

gr_1.Perspective=this.position

(2) htb_Rotation 的 moved 事件脚本。

gr_1.Rotation=this.position

(3) htb_Elevation 的 moved 事件脚本。

gr_1.Elevation=this.position

(4) cb_AddSeries 的 clicked 事件脚本。

if Trim(sle_Series.text)=’’ then

MessageBox( ’提示’ , ’请在编辑框里输入序列名!’ )

else

ddlb_Series.AddItem(sle_Series.text)

gr_1.AddSeries(sle_Series.text)

end if

(5) cb_AddData 按钮的 clicked 事件增加以下脚本。

Pow erBuilder9.0 基础应用与系统开发

— 184 —

integer SeriesNbr,i

SeriesNbr = gr_1.FindSeries(ddlb_Series.text) //根据下拉框的内容寻找序列

for i=1 to 10 // 增加 10 个随机数,随机编号的类别

gr_1.AddData(SeriesNbr,rand(10)+30,’A’+string(rand(15)))

next

(6) 在 ddlb_GraphType 的 selectionchanged 事件增加脚本,使下拉框选项被改变的时

候,马上改变统计图的显示格式。

choose case this.item[index]

case ’area3d!’

gr_1.GraphType=area3d!

case ’areagraph!’

gr_1.GraphType=areagraph!

case ’bar3dgraph!’

gr_1.GraphType=bar3dgraph!

case ’bar3dobjgraph!’

gr_1.GraphType=bar3dobjgraph!

. . . //这里列举各种显示格式,为节省篇幅起见,省略部分格式

. . .

case ’scattergraph!’

gr_1.GraphType=scattergraph!

case else

messagebox(’’,’unknown graphtype’)

end choose

这样,运行程序时,可先向统计图增加一些随机数,并选择不同的实现形式,进行该

统计图的旋转、视角变换等。

6.2.12 TreeView(树型视图控件)

TreeView(树型视图控件)使用树型结构显示数据,Windows 的文件夹窗口就是一个典

型的树型控件。

6.2.12.1 树型控件的属性

主要属性如下。

(1) EditLabels:选中该项后,可以通过单击被选中的节点来修改它的文本。

(2) HasLine:选中该项使用线条来联结各个节点,否则节点间无线条。

(3) HasButton:选中该项,节点前有+或-按钮。

(4) SingleExpand:选中该项,只允许被选中的节点展开。

(5) SortType:节点排序的类型,可以是升序、降序或不排序。

6.2.12.2 树型控件的节点

所谓树的节点是指两个或两个以上树枝的连接点。在进行树的生成、删除和查找等操

作的时候,都要先定义一个节点,或找到节点的句柄,然后对节点或句柄进行操作。树型

节点(TreeViewItem)有如下常用属性。

第 6 章 窗口与控件

— 185 —

(1) PictureIndex:节点的图标索引。

(2) SelectedPictureIndex:节点被选中时的图标的索引。

(3) StatePictureIndex:在图标左边还有一个图标表达图标索引的状态。

(4) Label:节点的文本。

(5) Data:节点所代表的数值,可以是任意类型。

(6) Children:是否有子树。

(7) Level:表示该节点在树型结构里的层数。

(8) Selected:表示节点是否被选中。

(9) ItemHandle:该节点的句柄,是一个长整型的数值,根节点的句柄是 1,其他节点

的句柄根据被插入的先后依次增加。

上 面 提 到 的 图 标 , 需 要 在 树 型 控 件 的 属 性 窗 口 进 行 设 置 , PictureIndex 和

SelectedPictureIndex 属性指向 Pictures 页所设置的图标列表的序号。而 StatePictureIndex

属性指向在 State 页所设置的图标列表的序号。树型控件和其他需要用到图标的控件一

样,既可以使用 PowerBuilder 预先设定的图标,也可以自己选择图标文件。

6.2.12.3 树型控件的函数

1. 插入节点的函数

(1) InsertItem(handleparent, handleafter, label, pictureindex)在 handleparent 的子树中,

handleafter 所表示的节点后面插入一个新的节点,其标号为 label,图标索引号为

pictureindex。

(2) InsertItemFirst(handleparent, label, pictureindex)在 handleparent 子树中的最前方插入

一个新的节点,标号为 label,图标索引号为 pictureindex。

(3) InsertItemLast(handleparent, label, pictureindex)在 handleparent 的子树中的最后插入

一个新的节点,标号为 label,图标索引号为 pictureindex。

(4) InsertItemSort(handleparent, label, pictureindex)按照当前的排序方法在 handleparent

的子树中插入一个节点,标号为 label,图标索引号为 pictureindex。

(5) 以上的函数都还有另一种参数格式,就是 func(handleparent, item),使用这种格式

需要先定义一个 item 型的对象,赋值后把它作为参数传入相关函数。

2. 删除节点的函数

DeleteItem(itemhandle)删除 itemhandle 所代表的节点。

3. 查找节点的函数

FindItem(navigationcode, itemhandle)是一个重要的函数,参数中的 navigationcode 是一

个枚举型的值,它表示了需要查找的节点与 itemhandle 所代表的节点之间的关系,

itemhandle 是一个基准节点的句柄,PowerBuilder 根据基准节点和查找的方向来进行节点

的搜索。如果查找成功返回目标节点的句柄,否则返回-1。

Navigationcode 可以是以下的数值之一。

(1) RootTreeItem!表示第 1 层的第 1 个节点。

Pow erBuilder9.0 基础应用与系统开发

— 186 —

(2) NextTreeItem!表示兄弟节点中的下一个节点。

(3) PreviousTreeItem!表示兄弟节点中的上一个节点。

(4) ParentTreeItem!表示父节点。

(5) ChildTreeItem!表示第 1 个子节点,如果基准节点被收缩了,这个参数会使基准节

点展开。

(6) FirstVisibleTreeItem!表示第 1 个可见的节点(不考虑该节点所在的层数)。如果控件

里有滚动条,被暂时隐藏的节点不会被搜索到。

(7) NextVisibleTreeItem!表示下一个可见的节点(不考虑该节点所在的层数)。

(8) PreviousVisibleTreeItem!表示上一个可见的节点,这个节点必须是被展开的。

(9) CurrentTreeItem!表示当前被选中的节点。

(10) DropHighlightTreeItem!表示最近被设置了 DropHighlighted 属性的节点。其中

DropHighlighted 属性表示该节点是拖动操作的目标对象。

FindItem 函数执行完成后不会自动选中节点,此时可以使用 SelectItem 函数或设置节

点的 Selected 属性来选中目标节点。注意,除非使用 ChildTreeItem!参数,FindItem 不会

把 已 经 收 缩 的 节 点 展 开 来 进 行 搜 索 。 如 果 navigationcode 参 数 是 RootTreeItem! 、

FirstVisibleTreeItem!、CurrentTreeItem!,或 DropHighlightTreeItem!,则可以把 itemhandle

属性设为 0。

4. 展开和收缩树的函数

(1) ExpandItem(itemhandle)展开 itemhandle 所代表的节点的下一层节点。

(2) ExpandAll(itemhandle)展开 itemhandle 所代表的节点下的所有节点,包括所有子

树。

(3) CollapseItem(itemhandle)收缩 itemhandle 所代表的节点的子树。

6.2.12.4 一个简单的示例

下面的例子简单地示范了 TreeView 控件的使用。

先按照图 6-22 所示在窗口上放置控件,其中左边的 TreeView 控件名称是 tv_1,然后

在 tv_1 的属性窗口里的 Pictures 标签页和 State 标签页添加 5 个图标。在窗口定义一个

integer 型的实例变量 ItemCount 用于记录第 1 层总的节点数。

图 6-22 树型控件的设计

第 6 章 窗口与控件

— 187 —

接下来编写脚本。

(1) 在 tv_1 的 Constructor 事件中编写 TreeView 控件的初始化脚本。

TreeViewItem ltvi_first

ltvi_first.label="Root"

ltvi_first.PictureIndex=4

ltvi_first.SelectedPictureIndex =5

ll_first=tv_1.InsertItemLast(0,ltvi_first)

if ll_first=-1 then MessageBox(‘提示’,’添加根节点的时候出错了’)

(2) 在按钮 cb_InsertItem 的 Clicked 事件中添加脚本,用于向 tv_1 插入一系列的随机

节点。

TreeViewItem ltvi_New,ltvi_Child

integer i,k

long l_new

For i = 1 To 10 //添加 10 个随机节点

ltvi_New.data=rand(500) //节点的值是一个 1 到 500 之间的随机数

ltvi_New.Label=string(ltvi_New.data)

ltvi_New.PictureIndex=1

ltvi_New.StatePictureIndex=1

ltvi_New.SelectedPictureIndex = 3

if ltvi_New.data>250 then //如果随机数大于 250,增加 5 个子节点

ltvi_new.children=true //有子树

l_new=tv_1.InsertItemLast(1, ltvi_New)

for k=1 to 5

ltvi_Child.data=rand(50) //子树节点的值也是一个随机数

ltvi_Child.Label=string(ltvi_Child.data)

ltvi_Child.PictureIndex=2

ltvi_Child.StatePictureIndex=rand(5)

ltvi_Child.SelectedPictureIndex = 3

ltvi_Child.Children=false

tv_1.InsertItemLast(l_new, ltvi_Child)

next

else

ltvi_new.children=false

tv_1.InsertItemLast(1, ltvi_New)

end if

ItemCount += 10 //记录第 1 层的总结点数

next

tv_1.ExpandItem(1) //把根节点展开

(3) 为按钮 cb_Expand 的 clicked 事件编写展开当前节点的脚本。

long ll_tvi

ll_tvi=tv_1.FindItem(CurrentTreeItem!,0)

if ll_tvi<>-1 then

tv_1.ExpandItem(ll_tvi)

else

MessageBox(’提示’,’找不到当前节点’)

end if

Pow erBuilder9.0 基础应用与系统开发

— 188 —

(4) 为按钮 cb_Collapse 的 clicked 事件编写收缩当前节点的脚本。

long ll_tvi

ll_tvi=tv_1.FindItem(CurrentTreeItem!,0)

if ll_tvi<>-1 then

tv_1.CollapseItem(ll_tvi)

else

MessageBox(’提示’,’找不到当前节点’)

end if

(5) 为按钮 cb_Delete 的 clicked 事件编写删除当前节点的脚本。

long ll_tvi

ll_tvi=tv_1.FindItem(CurrentTreeItem!,0)

if ll_tvi<>-1 then

tv_1.DeleteItem(ll_tvi)

else

MessageBox(’提示’,’找不到当前节点’)

end if

(6) 为按钮 cb_ShowHandle 的 clicked 事件编写显示当前节点句柄的脚本。

long ll_tvi

ll_tvi=tv_1.FindItem(CurrentTreeItem!,0)

if ll_tvi<>-1 then

messageBox(’当前节点的句柄是’,string(ll_tvi))

else

MessageBox(’提示’,’找不到当前节点’)

end if

(7) 为按钮 cb_Find 的 clicked 事件编写在第 1 层查找节点内容的脚本。

integer i

string s_item

long ll_tvi,ll_current1,ll_current

TreeViewItem ltv_current

s_item=sle_item.Text

if trim(s_item)="" or (NOT IsNumber(string(s_item))) then //检验输入内容

MessageBox(’提示’,’请输入一个数值’)

return 0

end if

ll_current=tv_1.FindItem(RootTreeItem!,0) //先找到根节点

ll_current=tv_1.FindItem(ChildTreeItem!,ll_current) //搜索第 1 个子节点

tv_1.GetItem(ll_current,ltv_current)

i = 0

do while (s_item<>trim(string(ltv_current.data)) or ll_current=-1 ) and

i<=ItemCount

i++

ll_current=tv_1.FindItem(NextTreeItem!,ll_current)

tv_1.GetItem(ll_current,ltv_current)

第 6 章 窗口与控件

— 189 —

loop

tv_1.SelectItem(ll_current) //选中目标节点

运行该程序,首先按 InsertRandomItem 按钮,生成一随机的树型节点;按 Expand 及

Collaps 按钮可展开或收缩该树;选中某节点并按 Delete 按钮可删除该节点;选中某节

点,按 ShowHandle 按钮,可显示该节点的句柄;在单行编辑框中输入节点内容,按 Find

按钮可进行搜索。

注意

TreeView 不提供统计节点数的函数,所以要用到一个实例变量来做记录,如果要统

计所有的节点,需要在插入节点和删除节点的时候修改这个变量,不过本示例中没有这样

做,读者可以自己完善它。此外,在整个 TreeView 里进行搜索,需要用到二叉树搜索的

概念,这里不作介绍。

6.2.13 其他控件

除了上述的几种常用控件之外,PowerBuilder 还提供了下述控件。

(1) MultiLineEdit(多行编辑框),用于输入超过一行的数据。

(2) RichTextEdit(丰富文本编辑框),是一个完整的文本编辑器,在里面可以为文本设

置不同的字体和颜色。

(3) Picture(图像)控件,用于显示一个图像文件,通常用于美化界面。目前支持的文件

格式是 BMP、JPG、RLE、GIF、WMF,在属性窗口的 PictureName 框选择一个文件即可。

(4) StaticHyperLink,PictureHyperLink(静态超链接和图像超链接),这是 PowerBuilder

提供的快速访问 Web 的控件,静态超链接显示一段文本,当单击事件发生的时候,会自

动打开默认的浏览器访问超链接所指定的 URL。

(5) VscrollBar,HScrollBar(垂直滚动条和水平滚动条),可以设置滚动条的最大值和

最小值,在脚本里可以用 Position 属性获得当前的位置,主要的事件是 moved、pageleft

和 pageright 事件。

(6) DataWindow(数据窗口控件),这是 PowerBuilder 最重要的一个控件,将在下一节

详细介绍。

6.3 数据窗口控件

6.3.1 概述

6.3.1.1 与数据窗口对象连接

数据窗口技术是 PowerBuilder 的核心技术之一,同样,数据窗口控件也是控件中的

核心控件,它是数据窗口对象和窗口的桥梁,通过数据窗口控件,用户可以享受到数据窗

口技术带来的方便。而开发人员则需要重点对数据窗口进行编程。读者要注意区分数据窗

口对象和数据窗口控件。

Pow erBuilder9.0 基础应用与系统开发

— 190 —

数据窗口控件需要和一个事务对象(一般是 SQLCA)联系,然后指定一个数据窗口。

为此首先要为数据窗口控件指定 DataObject 属性。假设数据窗口控件的名称是 dw_1,并

且 SQLCA 已经与数据库建立了连接,则可以在图 6-23 右边所示的属性窗口里选择设

置,并进一步编写脚本实现。

dw_1.SetTransObject(SQLCA)

dw_1.DataObject=�d_employee�

图 6-23 数据窗口控件的属性设置和脚本编写

有了以上的基础,就可以使用数据窗口功能强大的函数和丰富的属性了。

6.3.1.2 数据窗口控件的管理机制

用户在数据窗口对象看到的是一张用户视图,实际上 PowerBuilder 为这张视图维护

了 3 个缓冲区,它们分别是 Primary(主缓冲区)、Filter(过滤缓冲区)和 Delete(删除缓冲

区)。用户的操作只是对缓冲区的操作,当用户提交数据库变动的时候,数据窗口控件根

据缓冲区的标志来生成一系列的 SQL 语句,传递给 DBMS。

主缓冲区保存的是被过滤掉或被删除掉的数据,过滤缓冲区保存的是不符合过滤条件

的数据,删除缓冲区保存的是被删除掉的数据。当用户修改数据窗口的数据时,数据库的

内容并未修改,只有执行 Update(更新)操作后,缓冲区的数据才会反映到数据库中。

在脚本里可以用 GetItemStatus 来获得某一个数据的状态,用 SetItemStatus 来设置数

据的状态。数据的状态可以分为 4 种。

(1) New!表示新插入行的状态。

(2) NewModified!表示新插入行被修改后的状态。

(3) DataModified!表示原有的数据被修改后的状态。

(4) NotModified!表示原有的数据的初始状态。

在调用 Update 函数时,如果某一列的状态是 DataModified!,同时这一列是可更新

的,PowerBuilder 将为这一列生成一个 UPDATE 的 SQL 语句。对于状态为 New!和

第 6 章 窗口与控件

— 191 —

NewModified!的数据,都会生成 INSERT 语句。对于删除缓冲区的数据,PowerBuilder 会

生成 DELETE 语句。当然,如果有一行数据的状态是 New!或 NewModified!,然后被删除

了,则不会产生任何 SQL 语句。

6.3.1.3 数据窗口控件的事件

主要有如下几个事件。

(1) ItemChanged 事件,发生在用户修改了某一个字段的数据,然后移动了焦点或按

了回车的时候。这个事件通常用于数据校验,或触发其他数据的改动。此时 PowerBuilder

传入 3 个参数供开发人员使用:

· row 表示被修改的记录的行号。

· dwo 表示当前操纵的数据窗口的对象,可以用 dwo.name 获得对象的名称。

· data 表示该对象新的数据(字符串型)。

(2) EditChanged 事件,发生在用户修改了某个字段的时候。

(3) RowFocusChanged 事件,发生在用户将焦点移动到其他的数据行之后。

6.3.2 访问数据窗口的数据

可以使用数据窗口控件的函数或直接用数据表达式来访问数据窗口的数据。

6.3.2.1 使用函数

根据不同的数据类型,PowerBuilder 提供了一系列获取数据的函数,这些函数是:

GetItemDate 、 GetItemTime 、 GetItemDateTime 、 GetItemString 、 GetItemNumberh 和

GetItemDecimal。这些函数都有两种相同的参数格式。

(1) GetItemXXX(long row, integer column{,DWBuffer dwbuffer, boolean originalvalue})。

(2) GetItemXXX(long row, string column{, DWBuffer dwbuffer, boolean originalvalue})。

其中 row 是行号,integer 型的 column 是列号,string 型的 column 是字段名称,注意

这个字段名称是指在数据窗口里的对象名称,不一定跟数据库里的相同。dwbuffer 和

originalvalue 都是可选项,dwbuffer 表示使用的缓冲区,originalvalue 表示是否获取初始的

数据,默认情况下,dwbuffer=Primary!,Originalvalue=FALSE。

例如要将数据窗口中当前行的第 2 个字段的内容取出赋给 s_name 变量,可以用如下

脚本:

s_name=dw_1.GetItemString(dw_1.GetRow(),2)

将数据窗口中第 1 行的 emp_id 的初始值取出赋给 ll_id 变量,可以用如下脚本:

ll_id=dw_1.GetItemNumber(1,”emp_id”,Primary!,TRUE)

对应于 GetItemXXX 系列函数,有一个 SetItem 函数用于给数据窗口里的字段赋值,

格式为:

integer dwcontrol.SetItem ( long row, integer column, anyvalue )

integer dwcontrol.SetItem ( long row, string column, anyvalue )

Pow erBuilder9.0 基础应用与系统开发

— 192 —

这个函数在赋值的时候,不需要考虑类型,只要把要填写的数值放在 anyvalue 位置

就行了。

6.3.2.2 使用 Object 属性

数据窗口控件有一个重要的属性 Object,使用它可以直接访问数据窗口对象中的数

据。可以用以下 3 种格式来访问对象。

(1) dwcontrol.Object.Data {.buffer } {.datasource }[rownum, colnum]

可以实现对特定缓冲区的指定数据源的相应数据进行操作。其中:

· buffer 为可选项,有效值是 Primary,Delete,Filter 三者之一。

· Datasource 为可选项,有效值是 Current 和 Original 两者之一,Current 表示当前

值,Original 表示初始值。

· rownum 和 colnum 分别代表行号和列号,两者都是整数型。

以下表达式把主缓冲区的当前行第 1 列初始值赋予一个字符串:

s=dw_1.Object.Data.Primary.Original[dw_1.GetRow(),1]

(2) dwcontrol.Object.Data [ row, col]

此格式是格式1的缩写,它所访问的数据是针对主缓冲区当前值进行的。其中,col

可以是列的编号,也可以是列的名。

(3) dwcontrol.Object.ColName[row]

其中 ColName 表示列名,row 表示行号。

6.3.3 访问数据窗口的对象

也可以使用前述 dw_xxx.Object.objName 表达式来访问数据窗口里的对象。例如数据

窗口里有一个静态文本 st_title,可以用表达式 dw_1.Object.st_title.text 来访问它。

6.3.3.1 Object 属性

使用 Object 属性,这与访问数据很相似,格式为:

dwcontrol.Object.objname.property

其中 objname 是对象名,property 是属性名。如果要把数据窗口里的一个静态文本

t_1 隐藏起来,可以用如下的语句:

dw_1.Object.t_1.Visible=�no�

注意

布尔值可以用 TRUE 和 FALSE 来表示,也可以用 yes 和 no 来表示。

6.3.3.2 Describe 函数

PowerBuilder 使用 Describe 函数来访问数据窗口的对象属性。Describe 用于获得数据

第 6 章 窗口与控件

— 193 —

窗口对象的属性值。格式为:

DwControl.Describe ( propertylist )

其中 propertylist 是用空格隔开的对象属性列表。这些属性包括与数据窗口控件连接

的数据窗口对象中的所有对象的属性,以及数据窗口对象本身的属性。

对于数据窗口本身的属性可以用 DataWindow.Property 格式,而数据窗口里的对象属

性,则用 ObjName.Property 格式,例如:

s_quest=dw_1.Describe("datawindow.readonly datawindow.printer t_1.Text")

这个语句把数据窗口的 ReadOnly 属性和数据窗口的打印机属性赋予 s_quest,s_quest

的一个可能的值是 no~tEpson LQ-1600K on LPT1:~tNone,其中~t 表示一个 Tab。这个结

果表示数据窗口的 ReadOnly 属性为 FALSE,当前打印机为 LPT1 上的 Epson LQ 1600K,

数据窗口里的一个叫做 t_1 的静态文本的内容为 None。

数据窗口以及数据窗口里的对象的属性很多,这里不能一一阐述,请读者阅读

PowerBuilder 的 帮 助 , 可 以 在 目 录 中 的 DataWindow Reference|DataWindow Object

Properties|Controls in a DataWindow and their properties 里面找到详细的解释。

6.3.3.3 Modify 函数

Modify 函数同样是一个非常重要的函数,与 Describe 刚好相反,它的作用是对数据

窗口的对象进行属性设置,格式为:

result=dwcontrol.Modify ( modstring )

(1) 动态修改对象属性

可 以 同 时 修 改 多 个 属 性 , 此 时 modstring 的 格 式 是 : obj1.property=value1

obj2.property=value2⋯⋯,属性间用空格隔开。如果调用成功,函数返回一个空的字符

串,否则返回出错信息。下面的脚本对数据窗口里的静态文本 t_1 的边框属性和文本属性

同时更改:

string s_modify,s_result

s_modify="t_1.border=’6’ t_2.text=’none’"

s_result=dw_1.modify(s_modify)

if s_result="" then

MessageBox(’结果’,’成功地修改了属性!’)

else

MessageBox(’出错’,s_result)

end if

(2) 动态建立对象

这是充分反映 Modify 函数强大功能的一个用法,用于在运行时生成数据窗口的对

象,其中 modstring 的格式是:

CREATE object (settings)

Pow erBuilder9.0 基础应用与系统开发

— 194 —

该格式用于在数据窗口建立一个对象(例如文本、按钮、图像等,OLE 对象除外),其

中 object 是对象的类型,settings 是一个字符串,包含新建的对象的必要属性设置,不同

的属性之间使用空格隔开。

下面的脚本示例了如何在数据窗口里动态建立一个带边框的静态文本。

string s_create, s_result

s_create=’CREATE text(band=header alignment="1" ’ &

+ ’text="动态建立的对象" border="1" color="0" ’ &

+ ’x="50" y="40" height="64" width="576" ’ &

+ ’name=t_dymaic font.face="Arial" ’ &

+ ’font.height="-10" font.weight="400" ’ &

+ ’font.family="2" font.pitch="2" font.charset="0" ’ &

+ ’background.mode="1" background.color="536870912" )’

s_result=dw_1.Modify(s_create)

if s_result="" then

MessageBox(’恭喜’,’成功地建立了一个对象!’)

else

MessageBox(’遗憾’,’建立对象时出错了!~nPowerBuilder 返回的错误描述如下:

~n’+s_result) end if

(3) 销毁对象

使用如下的 modstring,可以销毁数据窗口里的对象。

DESTROY [COLUMN] object

其中 object 是对象的名称,如果该对象是一个列,则需要加上 COLUMN。以下脚本

用于销毁刚才建立的静态文本。

dw_1.Modify(’DESTROY t_dymaic’)

(4) 使用工具编写 Modify 和 Describe 函数

Modify 和 Describe 的功能十分强大,但是如果不熟悉数据窗口的对象属性,将会很

难编写它们的格式参数,初学者往往不知道哪些属性是必须传递给 Modify 函数的,而且

对象的外观属性也难以确定,往往要经过多次的测试才能确定。为了提高效率,

PowerBuilder 提供了一个语法工具帮助开发人员编写这些复杂的语句。

在主菜单选择 File|New,在弹出的窗口选择 Tool 标签页,如图 6-24 所示,在其中选

择 DataWindow Syntax,单击即可进入 DWSyntax Tool 的窗口,如图 6-25 所示。

选择菜单项 Syntax,选择需要编写的语句。有 3 种语句可供选择:Describe、Modify

和 SyntaxFromSQL。然后,在窗口里选择属性,填写属性即可轻松完成复杂的语句的编

写。

此外还有一种简便的方法,就是使用导出的数据窗口文件,摘出其中的对象描述语句

作为 Modify 或 Describe 的参数。步骤如下。

(1) 先建立一个临时的数据窗口。

(2) 在里面放置与需要动态建立的对象相同的对象。

第 6 章 窗口与控件

— 195 —

图 6-24 选择数据窗口语法工具

图 6-25 使用数据窗口语法工具创建 Modify 语句

(3) 保存这个数据窗口。

(4) 打开库画板,把该数据窗口导出为一个文件。

(5) 用文字编辑器打开导出的文件。

(6) 找到刚才建立的对象的描述,作为设置串即可。

6.3.4 数据窗口控件的事件

数据窗口控件拥有众多的事件,深入地理解这些事件有助于准确地控制数据窗口的行

为。一般来说,事件由用户的操作触发,不过某些函数也可以触发事件。在不同的事件的

处理过程中,PowerBuilder 传入不同的参数以方便开发人员进行控制,同时,这些脚本也

可以返回不同的值,PowerBuilder 会根据返回值进行相应的操作。因此,除了要掌握事件

的触发时机之外,还要恰当地设置返回值。如果某个事件有返回值,而脚本中没有指定返

回值,会默认地返回一个0。以下介绍数据窗口的常用事件。

Pow erBuilder9.0 基础应用与系统开发

— 196 —

6.3.4.1 Clicked 事件

用户在数据窗口控件上任何位置单击都会触发该事件。它提供了以下参数。

(1) xpos:鼠标光标的横坐标。

(2) ypos:鼠标光标的纵坐标。

(3) row:用户单击位置的行号。

(4) dwo:被鼠标单击中的数据窗口对象。

注意

这里的坐标不是屏幕坐标,它是以数据窗口控件的左上角为零点的相对坐标。可以用

dwo.name 来获得对象的名称,用 dwo.type 获得对象的类型。如果用户没有击中数据窗口

里面的任何对象,dwo 是指数据窗口本身,dwo.type 和 dwo.name 都是 datawindow。其他

的属性请参阅系统帮助内容。

以下脚本可以在用户单击一个列的时候自动按照该列的升序来排序,如果用户没有选

中一个列,则弹出一个消息显示这个对象的类型和名称。

string s_name,s_type

s_name=dwo.name

s_type=dwo.type

if s_type=’column’ then //如果单击的是一个列,按该列的升序排序

dw_1.SetSort( s_name+ " A")

dw_1.Sort()

else // 否则显示该对象的类型和名称

messagebox(s_type,s_name)

end if

6.3.4.2 DBError 事件

这是一个与数据库错误有关的事件。任何时候,由于调用数据窗口控件函数进行数据

库操作而导致数据库错误时,都会触发该事件。它有如下的参数。

(1) sqldbcode:由数据库提供的错误代码,长整型。代码含义可以参阅数据库厂商提

供的文档。假如数据库没有提供错误代码,sqldbcode 有如下的意义。

-1 表示由于事务对象中缺少了某些参数,未能连接到数据库。

-2 表示不能连接到数据库。

-3 表示 Update 或 Retrieve 中指定的键值与原有的值不匹配。

-4 表示向数据库写入 Blob 型数据时失败了。

(2) sqlerrtext:由数据库提供的描述错误的字符串。

(3) sqlsyntax:发生错误的 SQL 语句。

(4) buffer:错误发生时所在的数据窗口缓冲区。

(5) row:发生错误的数据行号。

第 6 章 窗口与控件

— 197 —

6.3.4.3 Error 事件

应用程序运行的时候使用了不正确的直接访问语法表达式时,就会触发错误事件,

PowerBuilder 对错误提供了丰富的描述,参数如下。

(1) errornumber:PowerBuilder 的错误编号。

(2) errortext:PowerBuilder 提供的错误描述。

(3) errorwindowmenu:引起错误的窗口或菜单名。

(4) errorobject:引起错误的对象。

(5) errorscript:引起错误的脚本。

(6) errorline:发生错误的脚本的行号。

(7) returnvalue:表示选择了 ExceptionSubstituteReturnValue!选项时的返回值,可以是

任何类型。

(8) action:这是一个枚举型的参数,类型为 ExceptionAction,它表示执行完 Error 事

件的脚本之后应用程序要做的工作。该参数的取值可以是以下的几种之一。

· ExceptionFail!表示触发 SystemError 事件。

· ExceptionIgnore!表示忽略发生的错误,请谨慎使用这个参数,因为忽略错误可能

导致其他的错误。

· ExceptionRetry!表示如果 OLE 服务器没有准备好,重新执行函数或检查表达式,

改选项对数据窗口控件不适用。

· ExceptionSubstitureReturnValue!表示使用在 ReturnValue 指定的值代替 OLE 服务器

或数据窗口返回的值,并取消错误条件。

6.3.4.4 ItemError 事件

如果为数据窗口的字段设置了校验表达式,用户在输入数据后,按 Enter 键或移走焦

点的时候,PowerBuilder 会根据表达式对数据进行校验,如果没有通过校验,就会触发

ItemError 事件。它有如下的参数。

(1) row:表示未能通过校验的数据行号。

(2) dwo:发生错误的数据窗口对象。

(3) data:发生错误的数据。

ItemError 事件有 3 种返回值,它们的意义分别如下。

(1) 0 是默认值,表示拒绝输入值,显示校验出错信息,同时不允许移走焦点。

(2) 1 表示拒绝输入值,同时不允许移走焦点,但是不显示出错信息。

(3) 2 表示接受输入值。

(4) 3 表示拒绝输入值,不显示出错信息,但是允许移走焦点。此时初始值会代替输

入值。

6.3.4.5 SQLPreview 事件

SQLPreview 事件发生在 PowerBuilder 把 SQL 语句传递给 DBMS 之前,这个事件经

常用于调试程序,参数有 request、sqltype、sqlsyntax、buffer、row,其中 sqlsyntax 是发

Pow erBuilder9.0 基础应用与系统开发

— 198 —

送给 DBMS 的 SQL 语句。可以修改 sqlsyntax,然后用 SetSQLPreview 函数把修改后的

SQL 语句传递给 DBMS。

SQLPreview 事件有 3 种返回值,它们的意义分别如下。

(1) 0 是默认值,表示继续执行。

(2) 1 表示停止执行。

(3) 2 表示跳过当前请求,接着执行下一个请求。

6.3.4.6 ItemChanged 事件

ItemChanged 事件发生在用户修改完一个数据项后按 Enter 键,或使该数据项失去焦

点并通过了校验之后。在数据窗口控件修改了数据项,调用 AcceptText、Update 函数之后

也会触发。可以用返回的值来决定 PowerBuilder 在脚本执行后的行为,返回值的意义如

一。

(1) 0 是默认值,表示接受当前数值。

(2) 1 表示拒绝输入值,不允许移走焦点。

(3) 2 表示拒绝输入值,但是允许移走焦点。

这个事件通常用于编写动态的校验规则。

6.3.4.7 其他常用事件

(1) ItemFocusChanged 事件发生在焦点从一个可编辑字段到另一个字段的时候。

(2) RowFocusChanged 事件发生在从某一行移动到另一行之时。

(3) RetrieveStart,RetrieveEnd 事件分别在检索数据(调用 Retrieve 函数)之前和之后触

发。

(4) UpdateStart,UpdateEnd 事件分别在更新数据(调用 Update 函数)之前和之后触发。

(5) PrintStart,PrintEnd 事件分别在开始打印之前和打印结束之后触发。

(6) PrintPage 在数据窗口的每一页被格式化之后、准备发送到打印机之前触发。

6.3.5 数据窗口控件的函数

6.3.5.1 数据检索函数

1. Retrieve 函数

该函数的作用是在打开的数据库中检索数据,发送到数据窗口中显示出来。其调用

格式为:

dwcontrol.Retrieve( {argument1, argument2, . . . } )

如果与数据窗口控件相关联的数据窗口对象中已经定义了参数,则需在 Retrieve 函数

调用时传入相同数量和顺序的参数。可以调用 DBCancel 函数中止检索。

注意

一旦调用检索函数,原来没有提交的数据将被全部冲掉,所以最好在检索之前检查有

第 6 章 窗口与控件

— 199 —

无修改过的数据,这可以通过调用 ModifiedCount 函数和 DeletedCount 函数来查验。

2. Sort 和 SetSort 函数

Sort 函数用于对数据窗口的列进行排序,它没有参数,调用之前需要调用 SetSort 函

数设置排序参数,SetSort 的格式是:

dwcontrol.SetSort(format)

其中 format 的格式可以是:

(1) # columnnumber order 例如:”#1 A” 或 “#1 A , #3 D”。

(2) #columnname order 例如:”#emp_id A”或 “#sales_order A , #cust_id D”。

格式中 A 表示升序,D 表示降序。

下面的例子对窗口中的数据按第 1 列升序、第 2 列降序进行排序。

dw_1.SetSort(�#1 A,#2 D�) dw_1.Sort()

3. Filter 和 SetFilter 函数

Filter 函数可以对数据窗口缓冲区的数据进行过滤,调用之前需要使用 SetFilter 函数

来设置过滤条件,SetFilter 函数的格式为:

dwcontrol.SetSort(format)

其中 format 是一个条件表达式,例如 Amount>500 AND Len(fname)>5。

4. GetSQLSelect 和 SetSQLSelect 函数

GetSQLSelect 函数用于获得当前数据窗口数据来源的 SQL 表达式,该函数没有参

数。SetSQLSelect 函数用于设置当前数据窗口的数据来源。

可能有的开发人员直接使用 Retrieve 检索一个数据来源的所有数据,然后用 Filter 函

数过滤来达到数据检索的功能,这种做法虽然代码简单易懂,而且也能实现功能,但是一

般不建议这样做,因为当数据量比较大的时候,调用 Retrieve 函数会导致巨大的网络数据

传输量,而且当数据传输到本地之后,还要为他们开辟一个巨大的数据缓冲区,会消耗太

多的系统资源。

一个更合理的做法是,先使用 GetSQLSelect 和 SetSQLSelect 函数来动态设置数据

源,或用更加简单的做法——使用带参数的数据窗口。

下面的例子选自一个学籍管理程序,该段脚本构成的函数用于根据查询条件动态检索

数据。

string s_filter=’’ //用于保存过滤条件 string ls_SQL //用于保存旧的 SQL 语句 ls_old=dw_1.GetSQLSelect() //得到旧的 SQL 语句 if s_kcdm<>’’ then //如果传入了课程名称,则作为过滤条件 s_filter=s_filter + ’ and t_cjb.kcdm="’+s_kcdm+’"’ end if if s_bjdm<>’’ then //如果传入了班级代码

Pow erBuilder9.0 基础应用与系统开发

— 200 —

s_filter=s_filter + ’and t_bjb.bjdm="’+s_bjdm+’"’

end if

dw_1.SetSQLSelect(ls_SQL + s_filter) //设置新的 SQL 语句

dw_1.Retrieve()

dw_1.SetSQLSelect(ls_SQL) //恢复旧的 SQL 语句

注意

最后一句是必要的,因为这一段脚本是一个函数,而这个函数会被多次调用,假如没

有最后一句,数据窗口的 SQL 语句会被错误地加上以前应用的过滤条件,而导致错误。

5. Find 函数

Find 函数在数据窗口的某个范围内查找符合一定条件的行,返回符合条件的记录的

行号。格式如下:

dwcontrol.Find ( expression, start, end )

其中参数 start、end 分别是开始和结束行号。如果 Find 函数查找到记录,则返回该记

录的行号,否则返回0。如果发生错误,则返回值的意义如下。

(1) -1 表示一般性的错误。

(2) -5 表示参数错误。

以下是为 cb_find 按钮的 Clicked 事件所编写的脚本,用于查找数据窗口中 zymc 字段

的长度大于 6 的记录。其作用是每单击一次 cb_find 按钮,就可以查找下一个符合条件的

记录,其中的变量 ll_current 是一个长整型的实例变量,初始值为1。脚本如下:

if ll_current<1 or ll_current>dw_1.RowCount() then ll_current=1

ll_current=dw_1.Find(’len(zymc)>6’,ll_current+1,dw_1.RowCount())

if ll_current>0 then

dw_1.ScrollToRow(ll_current)

dw_1.SetFocus()

else

if ll_current=0 then

MessageBox(’遗憾’,’没有符合条件的记录!’)

else

MessageBox(’发生了错误’,’错误号为~t’+string(ll_current) )

end if

end if

6.3.5.2 数据操作函数

1. Update 函数

Update 函数用于将数据窗口对象的缓冲区数据保存到实际的数据库中,PowerBuilder

会根据缓冲区的状态自动生成一系列 SQL 语句用于保存数据。调用格式为:

dwcontrol.Update ( { boolean accept {, boolean resetflag } } )

参数 accept 表示是否在保存数据之前调用 AcceptText 函数,默认情况下 accept 等于

第 6 章 窗口与控件

— 201 —

TRUE。AcceptText 函数用于保存正在编辑的字段内容。resetflag 用于确定是否在更新后

重置各个缓冲区的更新标志,默认值为 TRUE。通常情况下,只需简单地调用 Update()即

可。

2. InsertRow 函数

InsertRow 函数向数据窗口的主缓冲区插入一条新记录。调用格式为:

dwcontrol.InsertRow( row )

其中参数 row 表示被插入行的行号,若为 0,就插在 dwcontrol 最后一行的后面。

3. DeleteRow 函数

DeleteRow 函数在主缓冲区内删除一行。调用格式为:

dwcontrol.DeleteRow( row )

参数 row 为被删除行的行号,0表示当前行。被删除的行并不是真正被删除,只是

被移至删除缓冲区,在调用更新函数并提交了事务之后才会被真正删除。

4. RowsMove 函数

RowsMove 函数是一个功能很强大的函数,用于在数据窗口(相同的或不同的数据窗

口皆可)的缓冲区之间移动数据。调用格式为:

dwcontrol.RowsMove ( startrow, endrow, movebuffer, targetdw, beforerow,

targetbuffer )

其中 startrow 和 endrow 为长整型的起止行号,movebuffer 为移出数据窗口,targetdw

为目标数据窗口,beforerow 表示被移动的数据行在新的缓冲区的位置。targetbuffer 表示

移入缓冲区。这个函数可以用于实现数据操作的取消操作(Undo)功能。下面的函数调用将

dw_1 主缓冲区第10到20行的数据移到删除缓冲区第 1 行的位置:

dw_1.RowsMove(10,20,Primary!,dw_1,1,Delete!)

移动了数据之后,PowerBuilder 会自动重置缓冲区的更新标志。该函数还可应用于数

据存储,具体格式请参阅系统帮助。

6.3.5.3 行列函数

以下函数的使用都比较简单,功能如下。

(1) FilteredCount 函数,返回被过滤掉的行数。

(2) DeletedCount 函数,返回被删除掉的行数。

(3) ModifyedCount 函数,返回被修改过的行数。

(4) RowCount 函数,返回主缓冲区总的行数。

(5) ScrollToRow(row)函数,用于滚动到第 row 行,使 row 行成为当前行。

(6) SetRow(row)函数,使 row 行成为当前行。

Pow erBuilder9.0 基础应用与系统开发

— 202 —

6.4 用户对象

尽管 PowerBuilder 的开发环境已经提供了大量的可视对象和控件,但是用户的需求

是复杂多变的,系统提供的控件可能仍满足不了要求,此时,开发人员可以使用用户对象

定制符合一定要求的控件和对象。此外利用用户对象还可以封装对象,提高代码的重用效

率。

6.4.1 概述

6.4.1.1 用户对象的分类

打开菜单 File|New 或直接单击工具栏的 New 按钮,可以看到在 PB Object 页上第 1

排的所有用户对象,如图 6-26 所示。用户对象可以分为可视与不可视两大类。

图 6-26 新建用户对象

1. 可视用户对象

可视用户对象可以将一组控件组合在一起,或扩展原来的可视控件。它可以分成 3

类。

(1) 标准可视用户对象(Standard Visual)。是对 PowerBuilder 的标准控件的扩展,具有

标准控件的所有属性、方法和事件,开发人员可以对它们进行增加或扩展。

(2) 定制可视用户对象(Custom Visual)。可以是一系列控件、对象(包括用户对象)的组

合,开发人员必须自己定义各种属性、事件和函数。

(3) 外部可视用户对象(External Visual)。包含来自非 PowerBuilder 建立的用户对象,

当开发人员使用定制的动态链接库时,可以通过这种用户对象将其引入到当前应用来。

2. 不可视用户对象

不可视用户对象也叫类用户对象,用于定义一些能够重用的业务逻辑或常用的处理过

程。它包含以下两类。

第 6 章 窗口与控件

— 203 —

(1) 标准类(Standard Class)。继承自 PowerBuilder 内置的不可视类。

(2) 定制类(Custom Class)。由开发人员自己定义的不可视类。

6.4.1.2 可视用户对象的基本功能

一个未经任何修饰的可视用户对象,通常具有一些基本的属性、事件和方法,常用属

性 有 Visible 、 Border 、 Enabled 、 Control[] 、 Height 、 Width 、 X 、 Y 等 。 事 件 有

Constructor、Destructor、RbuttonDown 以及和拖动操作相关的事件。基本函数比较多,有

22 个,比较常用的有 TriggerEvent、SetFocus、SetRedraw 等。

6.4.2 创建用户对象

6.4.2.1 标准可视用户对象

前面介绍控件时提到,下拉列表框不能获得当前行号的函数,这里介绍如何扩展下拉

列表框,使它具有 GetIndex 函数。在图 6-26 所示的窗口中选择 StandVisual(标准可视用户

对象),PowerBuilder 会提示选择新建的用户对象的基础控件,其界面如图 6-27 所示,选

择 DropDownListBox(下拉列表框)作为基础控件,单击 OK 按钮,进入用户对象的编辑环

境。

图 6-27 选择基础控件

下拉列表框的 SelectionChanged 事件有一个参数 index 表示新的行号,因此扩展的做

法就是在该事件里用局部变量记录 index,需要时调用局部变量即可。

首先为新的用户对象增加一个长整型的实例变量 l_index,并赋予默认值 0,即声明:

long l_index=0

这个变量用于在用户改变下拉项时记录当前的行号,在用户对象的 SelectionChanged

事件里加上一个语句:

l_index=index。

新建一个函数,命名为 GetIndex,无参数,返回值为长整型,函数的内容只有一句:

return l_index

Pow erBuilder9.0 基础应用与系统开发

— 204 —

保存新建的用户对象,命名为 uo_DropDownListBox。这样在下拉列表框中调用此函

数,即可获得当前行号。

6.4.2.2 定制可视用户对象

下面通过创建稍稍复杂一点的用户对象——模拟的时钟来说明如何定制可视用户对

象。在图 6-26 中选择 Custom Visual(定制可视用户对象),直接进入设计窗口,如图 6-28

所示。接着按下述步骤进行。

图 6-28 定制可视用户对象

(1) 像设计一个窗口一样放置各种控件和用户对象,按照图中的格式安排各种控件,

其中右上角放置一个静态文本 st_time,使用下陷风格的边框,文本设置为“00:00:00”。

时针、分针和秒针的名称分别为 ln_hour、ln_min、ln_sec,时钟上的数字用静态文本表

示。

(2) 为新建的用户对象新增一个事件,命名为 TicTac,用以定时刷新 3 个指针的位

置,用户也可以使用这个事件来实现定时任务。TicTac 事件的脚本如下:

st_time.text=string(now(),�hh:mm:ss�) real min,hour,sec min=Pi(1)*/30*Minute(now()) //把角度转换为弧度 sec=Pi(1)*/30*Second(now()) hour=Pi(1)*/6*Hour(now()) + min/12 ln_min.endx=500 + 300*sin(min) //计算指针位置

第 6 章 窗口与控件

— 205 —

ln_min.endy=500 - 300*cos(min)

ln_sec.endx=500 + 300*sin(sec)

ln_sec.endy=500 - 300*cos(sec)

ln_hour.endx=500 + 250*sin(hour)

ln_hour.endy=500 - 250*cos(hour)

(3) 这里需要用到一个非可视对象 Timing,该对象有一个 Timer 事件,每隔一段时间

Timer 事件被自动触发,时间间隔可以在它的 Start 函数中指定。在主菜单的 Insert|Object

下选择 Timing 对象,命名为 MyTiming。

为 MyTiming 的 Timer 事件编写脚本,定时触发 TicTac 事件。

parent.TriggerEvent(’tictac’)

(4) 在用户对象的 Constructor 事件中编写脚本,初始化指针的位置,以免用户对象在

被调用的一霎那显示不正确的位置。

ln_hour.BeginX=500

ln_hour.BeginY=500

ln_min.BeginX=500

ln_min.BeginY=500

ln_sec.BeginX=500

ln_sec.BeginY=500

this.TriggerEvent(’tictac’)

(5) 为方便用户掌握启动时钟的时机,给用户对象增加一个函数 Start,参数为双精度

型的 Interval,脚本如下:

if MyTiming.Start(a_interval)<>1 then

MessageBox(’错误’,’计时器启动失败!’)

end if

至此,一个模拟时钟的用户对象已经设计完毕,保存为 uo_clock。需要启动时钟时,

调用 Start 函数即可。使用该用户对象的一个显示实例,如图 6-29 所示。

图 6-29 时钟用户对象的运行结果

Pow erBuilder9.0 基础应用与系统开发

— 206 —

6.4.2.3 不可视用户对象

不可视用户对象不涉及界面的设计,其创建步骤要简单一些。对于标准类用户对

象,首先选择一个基类,例如 Connection、PipeLine、Error 等,然后添加属性,编写用户

定义的函数、事件,完成后保存即可。对于定制类用户对象,不需要选择基类即可编辑。

6.4.3 使用用户对象

可以通过两种方式使用用户对象,一种是在设计阶段把用户对象放置在窗口上,另一

种是在运行期间动态创建和销毁用户对象。

6.4.3.1 设计阶段使用用户对象

在工具栏选择用户对象控件,或在主菜单下选择 Insert|Object|UserObject ⋯(见图 6-

30),在弹出的窗口选择一个可视用户对象,放于窗口即可使用该用户对象。例如要使用

前面创建的可视用户对象 uo_clock,可把 uo_clock 放在窗口之后,命名为 uo_1,在窗口

的 Open 事件加入语句:uo_1.Start(1),即可实现在窗口打开时启动用户对象时钟。

图 6-30 使用用户对象

6.4.3.2 运行期间使用用户对象

这种情况下要用到以下几个函数。

1. OpenUserObject 函数

OpenUserObject 函数用于在窗口创建一个用户对象,并且把它显示出来,它有以下两

种格式。

第 6 章 窗口与控件

— 207 —

(1) windowname.OpenUserObject( userobjvar { , x , y } )。

(2) windowname.OpenUserObject( userobjvar, userobjtype { , x , y } )。

其中 userobjvar 是一个用户对象变量,x 和 y 分别是该用户对象在窗口中的位置,

userobjtype 是用户对象的类型。第 2 种用法用于用户对象的类型不确定的情况,可以用字

符串变量来表示 userobjtype,x、y 的默认值都是 0。

2. OpenUserObjectWithParm 函数

OpenUserObjectWithParm 函数除了完成 OpenUserObject 的功能之外,还可以向用户

对象传递参数,参数存放在全局对象 Message 里。这种情况下,被调用的用户对象必须在

Constructor 事 件 里 读 取 Message 内 容 , 根 据 参 数 做 出 相 应 的 初 始 化 动 作 。

OpenUserObjectWithParm 函数同样有以下两种格式。

(1) windowname.OpenUserObjectWithParm( userobjvar, parameter { , x , y } )。

(2) windowname.OpenUserObjectWithParm( userobjvar, parameter, userobjtype { , x , y } )。

两个格式与 OpenUserObject 的两个格式相对应,只是多了一个参数 parameter。由于

Message 对象中用于传递参数的属性只有 3 个,Parameter 的类型必须是以下 3 种之一。

(1) 字符串型。

(2) 数字型。

(3) PowerObject 型。

3. CloseUserObject 函数

CloseUserObject 函数对于动态建立的用户对象,PowerBuilder 不会在程序结束的时候

自动关闭,必须使用 CloseUserObject 函数来销毁它。格式如下:

windowname.CloseUserObject ( userobjname )

一旦调用该函数,用户对象的销毁事件会被触发,该对象将从窗口中消失。假如没有

调用 CloseUserObject 函数,很可能会导致系统内存出现漏洞。

下面举例说明两函数的应用。

在窗口声明一个 uo_clock 类型的实例变量 uo_1,在 Open 事件创建用户对象。

uo_1=Create uo_clock

this.OpenUserObject(uo_1,50,50)

uo_1.Start(1)

在窗口的 Close 事件销毁用户对象。

this.CloseUserObject( uo_1 )

6.4.3.3 访问用户对象里的对象

用户对象里面包含了多个对象或用户对象,可以直接使用点操作符来访问这些对象和

调用函数。例如在上面的例子里,uo_clock 包含了一个名为 mytiming 的 timing 对象,可

以在窗口里引用它的 start 函数。

Pow erBuilder9.0 基础应用与系统开发

— 208 —

uo_1.timing.Start( )

对于定制的可视用户对象,里面还包含了若干控件,可以使用用户对象的 Control 属

性来访问这些对象。用户对象的 Control 属性的用法与窗口的 Control 属性相同,请读者

参阅前面的内容。

6.5 窗口与控件编程实例

本实例实现像 IE 浏览器那样可以自动记录历史网址,方便用户进行操作。另外,可

以对已保存的网址进行清除。

具体步骤如下。

(1) 创建主窗口 w_url,其中包括下拉列表框 ddlb_1,按钮 cb_add,按钮 cb_hsitory

和按钮 cb_exit。窗口界面如图 6-31 所示。

图 6-31 主窗口界面

(2) 在应用程序对象的 Open 事件中编写代码如下:

string ls

//获得程序运行的当前路径

ls=space(256)

ls=getcurrentdirectory()

gs_currdir=ls

open(w_url)//打开主窗口

(3) 在下拉列表框 ddlb_1 的 constructor 事件中编写代码如下:

dropdownlistbox lddlb

lddlb=this

int li

string ls_url,ls_urls,ls_fullpath

ls_fullpath=trim(gs_currdir)

if right(ls_fullpath,1)<>"/" or right(ls_fullpath,1)<>"\" then

ls_fullpath=ls_fullpath+"\"

end if

ls_fullpath=ls_fullpath+gs_inifile//从初始化文件中读取数据

ls_urls=trim(profilestring(ls_fullpath,"URL","url","[No urls]"))

第 6 章 窗口与控件

— 209 —

if right(ls_urls,1)<>"," then ls_urls=ls_urls+","

li=pos(ls_urls,",")

do while li>0 //往下拉列表中添加表项

ls_url=trim(left(ls_urls,li -1))

ls_urls=replace(ls_urls,1,li,"")

lddlb.insertitem(ls_url,1)

li=pos(ls_urls,",")

loop

(4) 在下拉列表框 ddlb_1 的 modified 事件中编写代码如下:

int li

string ls

ls=text

//在下拉列表框查找是否已有正要添加的内容,有则不添加,没有则添加

if trim(ls)<>"" then

li=finditem(text,0)

if li<=0 then

li=additem(ls)

end if

end if

(5) 在下拉列表框 ddlb_1 的 selectionchanged 事件中编写代码如下:

string str_null,str_file

setnull(str_null)

str_file=trim(ddlb_1.text)

//打开浏览器,激活选定的网址

shellexecuteA(handle(parent),str_null,str_file,str_null,str_null,1)

(6) 在命令按钮 cb_add 的 clicked 事件中编写代码如下:

int li

string ls_url,ls_urls,ls_fullpath

ls_fullpath=trim(gs_currdir)

if right(ls_fullpath,1)<>"/" or right(ls_fullpath,1)<>"\" then

ls_fullpath=ls_fullpath+"\"

end if

ls_fullpath=ls_fullpath+gs_inifile

for li=1 to totalitems(ddlb_1)

ls_urls=ls_urls+ddlb_1.text(li)+","

next

//保存下拉列表框的数据到初始化文件 web.ini 中去

//messagebox("",ls_urls)

if ls_urls<>"" then

setprofilestring(ls_fullpath,"URL","urls",ls_urls)

end if

(7) 在命令按钮 cb_history 的 clicked 事件中编写代码如下:

int li

Pow erBuilder9.0 基础应用与系统开发

— 210 —

string ls_fullpath

ls_fullpath=trim(gs_currdir)

if right(ls_fullpath,1)<>"/" or right(ls_fullpath,1)<>"\" then

ls_fullpath=ls_fullpath+"\"

end if

ls_fullpath=ls_fullpath+gs_inifile

//清空初始化文件和下拉列表框中的数据

setprofilestring(ls_fullpath,"URL","","")

ddlb_1.reset()

(8) 在命令按钮 cb_exit 的 clicked 事件中编写代码如下:

close(parent)//退出程序

最后,运行程序,主界面如图 6-32 所示。

图 6-32 程序运行主界面

6.6 练习题

(1) 假定要在打开窗口时向其传递两个参数,它们对应的数据类型分别是 Int 和

Char,请问怎样才能把这些参数传递给窗口?请写出在窗口中解析这些参数的脚本。

(2) 试创建一个带有命令按钮的窗口,当改变窗口的大小时,命令按钮的大小也随之

同比例改变。

(3) 在 PowerBuilder 中如果一个数据窗口对象的数据源来自两个或两个以上的表,对

于这种数据窗口,PowerBuilder 默认是不能更新的。如何通过编码的方式使这个数据窗口

能够更新?

(4) 请通过调用 API 函数实现数据窗口控件的最大化、最小化、隐藏和显示的功能。

(5) 以一个学校的各个学院作为第 1 层,对于每一个学院的各个系作为第 2 层,每一

个系中的各个教职工作为第 3 层,生成一个有关各个学院、系及其教职工的树结构。

(6) 设计一个有关计算机学院的学生成绩查询系统,要求计算出每一个学生的平均成

绩和全院学生各个学科的平均成绩。

(7) 试着不通过窗口中的 OLE 控件,而是在脚本中操纵 OLE 对象,实现把数据窗口

中的数据导出到 Word 文档中去,从而实现 PowerBuilder 与 Word 之间的通信和数据交

换。

第 7 章 使用库管理项目

PowerBuilder 使用库来管理应用程序,它把所有与应用程序相关的对象组织在库文件

中,这样便于集中管理和维护。对于小型应用程序来说,对象不太多,库管理的作用不是

十分明显。但是对于一个大型应用,如果没有一个统一的管理中心和管理准则,管理和操

作的复杂程度是难以想象的。

本章将详细介绍如何使用库来管理项目,以提高开发应用程序的效率。

7.1 库的概念

7.1.1 基本概念

在 PowerBuilder 中,库的概念相当重要。简单地说,库就是将 PowerBuilder 中所有

对象汇总起来,并进行综合管理的地方。当创建一个新的应用程序时,PowerBuilder 就产

生一个库文件(.pbl),自动管理应用程序中的各种对象。当保存一个对象(如一个窗口或是

一个菜单 )时,PowerBuilder 就将这个对象放入库中,同样,当打开一个对象时,

PowerBuilder 也是从库中检索出这个对象的。如图 7-1 所示说明了库中可存放的各种对象

以及存取库的过程。

图 7-1 存取库示意图

7.1.2 生成及命名

在创建一个应用程序时,PowerBuilder 会自动生成一个在默认路径下与该应用同名的

库,其后缀名为 pbl。当然,也可以自己修改该路径和库名,如图 7-2 所示。库的名称应

具有实际意义,这样,在管理时会带来很多方便。例如:在一个人员管理系统中,采用如

下库名:manager.pbl、mployee.pbl、salary.pbl,这样的命名含义清晰,会简化应用程序的

开发。

Application(应用)

Data PipeLine(数据管道)

Data Window(数据窗口)

Function(函数)

Window(窗口)

Menu(菜单)

User Object(用户对象)

Structure(结构)

Query(查询)

PowerBuilder Library (pbl 库) (存储应用程序中的对象)

保存对象

打开对象

Pow erBuilder9.0 基础应用与系统开发

— 212 —

图 7-2 创建新应用和库

7.1.3 搜索路径

库的搜索路径指出 PowerBuilder 按照指定的顺序搜索库。由于定义好的各种对象都

存放在库中,这样,当想要打开一个对象时,PowerBuilder 就会在 Library List(库列表)中

按照顺序搜索,直到找到为止。例如,有一个数据窗口,它是库列表中第 5 个库中的对

象,在想要打开它时,PowerBuilder 会按库列表的顺序从第 1 个库往下搜索,直到第 5 个

库。它将第 5 个库中的该对象打开,并停止搜索。如图 7-3 所示是库列表的示意图。在

Liarbry Search Path 中,可以输入库的路径及名称,并以分号相隔。也可以使用 Browser

按钮,选择自己需要的库。建议在库列表中最好按照合理的顺序排列库,这样可以提高效

率,减少搜索时间。例如:在应用程序开发的某段时间内,可能需要频繁地对某个库中的

对象进行保存或打开操作,就可以将这个库放在库列表的最前面,这样,当进行上述操作

时,PowerBuilder 只需检索少数几个库,就可以快速找到要操作的对象。

图 7-3 库列表示意图

第 7 章 使用库管理项目

— 213 —

7.2 库的组织原则

7.2.1 库的分配

PowerBuilder 中并没有规定一个应用程序中应该包含多少个库,以及每个库中应包含

多少个对象。对于一个比较小的应用程序,也许一个库就可以满足要求。对于大型的应用

程序,一个库是远远不够的,应将库进行细化,分成比较合理的一个个子库。

7.2.2 库的大小

每一个子库的大小在 PowerBuilder 中并无限制,考虑到其性能和方便,建议遵循以

下原则。

(1) 对象的数量:一般而言,库中对象的个数对库的性能没有任何影响。但是,为了

方便操作,建议在一个库中不要超过 50 至 60 个对象。因为,库的管理是在库画板中进行

的,在库画板中,库以树型显示,而对象是以列表型式显示的,如果一个库中对象太多,管

理起来非常不便,用户将不得不频繁地使用滚动条,这样会使浏览和修改显得十分麻烦。

(2) 平衡原则:应保持库与对象个数的平衡。也就是说,不能为了使库中的对象数量

少,却导致建了许多库,而每个库中也仅仅有少数几个对象的情况。这样会导致库列表中

的库搜索路径太长,以致 PowerBuilder 为查找一个对象需要检索太多的库,会使运行速

度变慢。

7.2.3 库的组织原则

良好的库组织和具有实际意义的库名称,对于应用程序的开发,是大有裨益的,这有

助于理清思路。在拥有多个库或多人开发应用程序时,这一点尤为重要。因此,对于库的

组织,应该加以重视。

通常情况下,可以按照库中对象的类型来划分库。比如可以将所有数据窗口放入一个

库,将所有窗口放入另一个库。也可以将应用程序划分成子系统或子模块,将每一个子系

统中的对象放入一个库。比如可以将一个人事系统划分为部门子模块、员工子模块等,这

样,就可以相应的将库划分为 department.pbl,employee.pbl 等。当然,也可以结合上述两

种方法,按照功能来划分整个系统,对于每个子系统再依据对象类型划分。至于究竟怎样

划分库,则视具体情况而定。

如果一个系统的功能并不复杂,可以划分成较少的子系统,但是它拥有相当多的对象

时,最好按照功能划分,以减少库的数量。如果功能复杂,而其对象较少时,则可以按照

对象的类型来划分。

另外,值得注意的是:在创建对象时,最好写上注释,这样,在管理库时,会带来很

多方便,避免了由于对象太多而导致混淆的麻烦。

7.2.4 库的优化

应该经常对库进行优化。优化可以移去库间的缝隙(物理上)和存储对象产生的碎片,

Pow erBuilder9.0 基础应用与系统开发

— 214 —

因而可以达到提高磁盘操作效率的目的。当然,优化只是影响数据在磁盘上的布局,并不

重新编译对象,因此对象的内容是不会发生任何变化的。通常,一个星期左右就应该对库

进行一次优化。

7.3 库画板

7.3.1 使用库画板

7.3.1.1 库画板

库画板就是开发人员对库进行管理的视图。通常,将库画板和系统树窗口结合起来显

示和管理工作区、目标、库以及它们所包含的对象。在库画板中,库文件(PBL)和它所包

含的对象使用两种风格来显示。它们分别是 Tree View(树型视图风格)和 List View(列表视

图风格)。默认情况下,库画板在左边以 Tree View 来显示库,而右边以 List View 显示库

所包括的对象。当打开库画板时,两种视图显示的都是计算机的驱动器以及其中所包含的

文件夹。如图 7-4 所示是库画板打开时所显示的内容。由此可以找到库所存放的位置并将

其打开。在显示打开的库时,Tree View 和 List View 都列出了当前库及其所包含的对象,

可以使用 Tree View 和 List View 中的一个进行所有库管理的操作,如图 7-5 所示是库打开

时两个视图的内容。

图 7-4 库画板打开时的显示内容

图 7-5 打开库时 Library Painter 显示的内容

第 7 章 使用库管理项目

— 215 —

使用两种视图方式中任何一种显示都可以管理库。Tree View 的特点是结构和层次分

明;List View 中不但列出了库中对象的名称,还列出了一些细节,如修改时间、编译时

间、对象的大小以及对该对象所做的注释等,这便于查询和管理。

系统树窗口中的工作区标签页类似于库画板中的树视图,可以在树视图中或是系统树

中完成大部分操作。系统树窗口不仅在库画板中可以十分方便地管理对象,而且它在整个

系统的开发过程中,都是一个控制中心。系统树窗口如图 7-6 所示。使用系统树窗口的好

处之一是,始终看到的是系统树的一个实例,它从始至终管理着对象。库画板则不是,每

次单击 Library Painter 图标都将打开它的一个实例。但是要注意的是,当系统树窗口和库

画板同时打开时,菜单和工具栏都只对库画板起作用。

图 7-6 System Tree(系统树)窗口

7.3.1.2 库画板的功能简介

对库的管理都是在库画板中进行的。可以使用工具栏打开库画板。Library Painter 图

标如图 7-7 所示(图中 Library Painter 图标以椭圆标出)。单击该图标,就打开了如图 7-4 所

示的库画板。在库画板中,可以作如下操作。

图 7-7 在工具栏中打开库画板

(1) 创建一个新库,删除库。

(2) 复制、移动、删除库中的任何一个对象。

(3) 在当前工作区的 Target(目标)中创建新对象。

(4) 打开库画板中包含的任一对象(包括应用程序对象,这样可以方便的改变当前应

用)。

Pow erBuilder9.0 基础应用与系统开发

— 216 —

(5) 使用 check-out(检出)和 check-in(检入)工具来控制库版本,具体操作将在 7.4 节中

介绍。

(6) 对库进行优化。

注意

在库画板中,不能打开不在库列表中包含的对象,也不能创建一个新对象以及给库改

名。下面介绍如何进行上述操作。

· 若想创建一个新库,选择库画板上的菜单项 Entry|Library|Create,这时会弹出一个

创建库的窗口,如图 7-8 所示。

图 7-8 Create Library(新建库)对话框

如图 7-8 所示椭圆标记的地方,输入要建立的库名,单击“保存”按钮,系统自动

以.pbl 后缀保存。这时系统会弹出另一对话框,让用户输入对库的注释,如图 7-9 所示。

千万不可小看注释,它会为用户减少很多以后不必要的麻烦。

图 7-9 为新建库写注释

· 删除一个库,既可以使用菜单 Entry|Delete 将选择的库删除,也可以选中想要删

除的库右击,在弹出的菜单中选择 Delete,将库删除。进行库的删除操作时,一定要谨

慎,因为库中的所有对象将会被一同删除。

· 在当前工作区的 Target(目标)中,可以创建新对象。在系统树中选择 Target(目标)

第 7 章 使用库管理项目

— 217 —

右击,在弹出菜单中选择 New,系统会弹出创建新对象窗口。

· 复制库中对象时,选中想要复制的对象右击,在弹出菜单中选择 Copy,这时会弹

出一个 Select Library(选择库)对话框,如图 7-10 所示。在列出的所有库中,选择想要复制

到的目标库,单击“打开”按钮,完成复制。

图 7-10 选择复制的目标库

· 移动库中对象时,选中想要移动的对象右击,在弹出菜单中选择 Move,会弹出

一个与图 7-10 相同的对话框,选择想要移动到的目标库,完成移动。复制和移动的区别

在于:复制是生成该对象的一个副本,将其保存到目标库。而移动是将对象进行转移,并

不生成对象副本。

· 删除库中对象时,选中想要删除的对象右击,在弹出菜单中选择 Delete,就可以

删除对象。

· 若想打开选中的对象,只需双击它即可,或是在该对象上单击鼠标右键,在弹出

菜单中选择 Edit 项。

· check-out(检出)和 check-in(检入)工具的使用,将在 7.4 节进行介绍。

· 对库进行优化可以提高性能。在 Tree View 中,选中想要优化的库并右击,在弹

出菜单中选择 Optimize,系统将自动对库进行优化。

以上是库画板的基本操作,下面介绍其他一些常用操作。

1. 列表视图中控制列的显示

可以选择是否在列表视图中显示修改日期、对象大小、注释等信息。选择菜单中的

Design|Option 选项,弹出如图 7-11 所示的对话框。在 General 标签页里,可以选择和清

除 Display 项目中的 Modification Date, Compilation Date, Sizes, and Comments。这样就可以

只显示出所要的内容,使界面更加清晰。

2. 选择对象

在列表视图中,可以一次选择多个对象。如果所选的多个对象不连续,可以使用

Ctrl+Click,如果选择连续多个对象,可以使用 Shift+Click,这时,先在第 1 个对象上使

用 Shift+Click,再在最后一个对象上使用 Shift+Click,这样,中间所有的对象都作为选中

目标。还可以更方便地选中所有对象,选择菜单上的 Entry|Library|Select All 菜单项(或按

快捷键 Ctrl+A),将一次选中所有对象。

Pow erBuilder9.0 基础应用与系统开发

— 218 —

图 7-11 选择是否显示控制列

3. 过滤显示对象

在默认情况下,库画板显示的是库中所有的对象,包括工作区、目标、文件夹以及文

件。但是也可以指定库中显示某些对象,也就是说,可以在 List 中只显示窗口对象(或是

其他的任何对象,甚至可以指定显示以某字母打头的窗口对象,这在对象很多的情况下是

非常方便的)。选择菜单中的 Design|Option 选项,在 Include 标签页中进行设置。如图 7-

12 所示显示了 Include 标签页。

图 7-12 过滤显示对象

在 Entry Types 下的复选框内,选中或取消想要列出的类型(系统默认情况下将全部选

中)。为了显示包含具体文本的某些对象,可以在 Name 文本框中输入文本。例如只想显

示以 h 开头的数据窗口,可以在 Name 框中,输入 d_h*。这里可以使用通配符*以及?,

其中*代表一个字符串,?只代表一个字符。单击 OK 按钮,可以看到,库画板中已经过

第 7 章 使用库管理项目

— 219 —

滤了显示对象。

在 Tree 和 List 视图中,还可以重新设置库中显示的对象。选定一个库,在单击鼠标

右键弹出的菜单中,可以清除或选中显示对象,如图 7-13 所示。

图 7-13 弹出菜单

4. 当前库

在 PowerBuilder 中,当前库指的是当打开或是继承一个对象时,系统默认指定的那

个库,也就是包含最近被编辑的对象的库。在 Tree View 或 List View 中单击一下获得焦

点,再选择菜单中的 View|Most Recent Object,在 Tree View 中的当前库会高亮显示,而

在 List View 中会随之显示库中的所有对象。

5. 设置根位置

在 Tree View 和 List View 中,可以设置根目录的位置。选择菜单中的 View|Set Root

选项,会弹出如图 7-14 所示的对话框。在 Set root to 中选择想将其作为 Tree View 或 List

View 根位置的选项,如果选择 Directory 和 Library 作为根,还可以在 Path 中输入路径。

图 7-14 设置根位置

设置视图根位置的好处就在于可以更加方便打开所要找寻的库。比如用户的库存放在

硬盘的某一盘区的一个文件夹中,为了打开所需要的库,每次进入 Library Painter(库画

板),都会以默认的我的电脑作为根,如图 7-4 所示,然后再找到该文件夹,这样比较麻

烦。如果用户将包含常使用库的目录(Directory)作为打开库画板后的根,这样就方便多

了,如图 7-15 所示。如果使用不在一个地方的多个库,也可以将当前工作区作为根位

置,因为它能使所有库显示在一个树下。

7.3.2 搜索库及其中的对象

根据指定的字符串,可以像 Word 或其他工具一样,在 PowerBuilder 中进行搜索。可

Pow erBuilder9.0 基础应用与系统开发

— 220 —

以依据指定的字符串在指定的目标中查找相应的内容。例如想在库实体中查找对象,只需

选中该库实体,再选择弹出菜单中的 Search,系统将默认在此库实体中搜索对象。还可以

在目标甚至在某一对象中查找内容。

图 7-15 以 Library List 作为根查找库

可以查询以下内容。

(1) 使用 OpenSheet 函数的所有脚本(Script)。

(2) 所有包含命令按钮 Cb_exit 的窗口。

(3) 在数据库中,存取某一表(如 T_Employee 表)的所有数据窗口。

下面介绍如何进行上述搜索。

在想要进行搜索的实体上单击右键,弹出如图 7-16 所示的对话框。在 Search For 文

本框中,输入想要查找的字符串。输入的字符串可以是脚本或是变量中使用的词组、单

词,甚至可以是某一单词的几个字母。注意:输入字符串中不可使用通配符。

图 7-16 在库中进行搜索

Match Upper/Lowercase 复选框指定查找时是否区分大小写。

在 Display 选择框中,选择想要在结果中显示的内容,这些内容包括:查找字符所在

的对象名(Entry Name)、行号(Line Number)、控件名(Control/Item Name)、查找内容所在

行的内容(Line Where Found)、事件名(Event Name)。

在 Search In 选择框内,可以选择在什么地方查找,如在特性、脚本或是变量中进行

查找。

单击 OK 按钮,PowerBuilder 便开始进行搜索。可以在状态条中看到其检索过程。查

找 结 果 将 显 示 在 库 画 板 底 部 的 Output( 输 出 ) 窗 口 中 。 如 图 7-17 所 示 显 示 了 以

SetTransObject 为关键字时在脚本中进行查找的结果。找到所需要的结果后,可以利用该

第 7 章 使用库管理项目

— 221 —

结果进行以下操作。

图 7-17 搜索结果

(1) 通过双击结果内容中的某一项,或单击该项,再选择弹出菜单的 Edit,直接进入

该对象所在的画板。

(2) 打印结果窗口中的内容。

(3) 将查询结果复制到一个文本文件中。

7.3.3 重新生成库实体

在 PowerBuilder 中,若有如下情形需要重新生成库实体。

(1) 当修改了祖先对象时,就可以 Regenerate(重新生成)子孙对象,这样子孙对象就

能获得其祖先的最新修改的属性。

(2) 当对应用程序作了外部修改后,可以 Rebuild(重建)整个库,这样就能重新生成库

中相关的对象。

(3) 当将 PowerBuilder 升级到一个新版本的时候,就需要 Migrate(移植)应用程序。当

重新生成一个实体时,PowerBuilder 会对其重新编译,然后用编译过的形式代替旧版本。

下面简要介绍一下 Regenerate、Rebuild、和 Migrate。

1. 重新生成 Regenerate(子孙对象)

选择想要重新生成的实体 (比如选择一个数据窗口 )右击,在弹出菜单中选 择

Regenerate,或是在菜单中选择 Entry|Library Item|Regenerate,系统将自动重新生成所选对

象。实际上,PowerBuilder 使用 Source 格式(Source 是 PowerBuilder 中存取对象的一种格

式,它是一种文本的、可读的代码,详细描述了对象的所有属性。)来重新编译库实体,

然后用最新编译过的对象来代替原对象,同时 PowerBuilder 将修改编译日期和文件大

小。

如果有很多对象继承于一个祖先,还可以使用浏览画板方便地重新生成这个祖先的所

有子孙对象。打开浏览画板图标,如图 7-18 中椭圆所示,出现如图 7-19 所示的窗口,选

择想要重新生成的对象类型。例如,想重新生成 w_fatherinpu 父窗口所有的子孙对象,可

以选择 window 标签页。选择祖先对象右击,在弹出的菜单中先选中 Show Hierarchy(显示

继承),然后选择 Regenerate。选择显示继承后,可以通过双击 w_fatherinput 前的窗口图

标,看到继承于它的所有子孙对象,这为 PowerBuilder 重新生成一个祖先对象的所有子

孙提供了一种简便的方法,它可以按此顺序将这些对象重新生成。

图 7-18 Browsr 画板的图标

Pow erBuilder9.0 基础应用与系统开发

— 222 —

图 7-19 重新生成库中对象

注意

当一次重新生成多个对象时,PowerBuilder 将按照它们在库中的顺序进行,这就有可

能产生错误。比如选择的多个对象中,包含祖先对象和它的一些子孙对象,有可能子孙对

象在库中的顺序先于其祖先,这样,它会先重新编译,这时祖先的修改就无法映射到它,

使它不能够重新继承。因此,应该使用 Rebuild(重建)同时将这些对象更新。

2. Rebuild(重建)工作区和目标

当对目标进行修改后,需要更新一个或多个库时,应该使用 Rebuild(重建)选项,按

照正确的顺序更新库对象,以避免上述情况产生的错误。当需要重建一个工作区或库的时

候,可以使用两种方法。

(1) 增量重建:每次都参考上次修改过的对象和库进行更新。

(2) 全部重建:将对象和库全部进行更新。

根据需要,如果要重建工作区,在工具栏中选择增量重建工作区图标 ,或是全部

重建工作区图标 ,也可以在系统树或库画板中选择工作区,在弹出菜单中选择

Incremental Rebuild Workspace 或 Full Rebuild Workspace。

如果要重建目标,选中该目标,在菜单中选择 Entry|Target|Incremental Rebuild,或是

Entry|Target|Full Rebuild,同样,也可以使用弹出菜单进行选择。系统开始自动进行重建

后,状态条上会显示生成和重建的整个进程。

3. Migrate(移植)应用程序

当 PowerBuilder 需要升级的时候,用户可能想将应用程序移植到新版本下,使开发

在新版本中继续进行,这就要用到 Migrate。

在通常情况下,当打开一个工作区,其中包含需要移植的目标,或是向工作区中加入

需要移植的目标时,PowerBuilder 会自动提示移植目标。但是,有些时候,必须自己手动

移植。比如当向库列表中添加库时,如果该库没有经过移植,其中的对象是无法打开的,

为 了 能 够 正 常 工 作 , 必 须 手 动 移 植 库 。 选 中 想 要 移 植 的 目 标 , 在 菜 单 中 选 择

第 7 章 使用库管理项目

— 223 —

Entry|Target|Migrate,将弹出 Migrate Application(移植应用程序)对话框,如图 7-20 所示。

图 7-20 Migrate Application 对话框

Library List To Migrate 列出了当前准备移植的应用程序中的所有库,也可以使用

Browser 按钮进行选择。

Messages 组中有两个选项,它们都用来显示产生的错误或警告信息。其中,

Information 用来显示移植时的情况信息,可以将它保存到文件中,或是打印输出。

Obsolete 用来显示应用程序旧代码的信息。

单击 OK 按钮,系统开始移植应用程序。

注意

最好在移植应用程序前对所有库进行备份。

7.3.4 导出和导入实体

在 PowerBuilder 中,可以以文本文件形式导出库和对象的定义,该文本文件包含了

定义一个对象的全部内容。文件与库中定义对象所用的 Source 格式使用了同样的语法。

导出对象的主要原因在于,当需要对库及库中的对象进行备份,或想将其转移到另外一台

计算机上,采用导出和导入操作比较方便。因为文本文件几乎是所有机器都可以接受的格

式,等到有合适的条件,再将这些文本文件转换成 PowerBuilder 所用的格式,也就是进

行导入操作。其次是由于文本文件存取方便而且所占存储空间小。

需要注意的是:进行导出或导入操作,其目的只是在于将 PowerBuilder 格式的文件

转换成文本文件方便保存,而不是修改它。虽然可以使用源编辑器直接修改源代码,但是

建议最好不要对导出的文本文件进行修改。

7.3.4.1 导出实体

选择想要导出的实体右击,在弹出菜单中选择 Export,或选择菜单中的 Entry|Library

Item|Export,系统弹出如图 7-21 所示的对话框。如果有多个对象同时导出,在文件名文

本框中,显示的是第 1 个对象的文件名。在保存类型文本框中,系统将根据选择的对象类

Pow erBuilder9.0 基础应用与系统开发

— 224 —

型为其加上合适的后缀(后缀的格式为.srx,其中 x 表示保存对象的类型,当导出数据窗口

对象时,x 为 d,即后缀名为.srd,对于窗口对象,后缀名为.sdw,其余类似),注意不要

修改系统自动提供的后缀名。在图 7-21 中为导出文件指定存放路径,单击“保存”按

钮,PowerBuilder 会将指定文件转换成文本格式,然后接着显示第 2 个需要转换的实体,

重复上述操作,直到转换完所有实体。

图 7-21 导出库实体

注意

如果库画板的设置中可以显示文件(如图 7-12 中 File 复选框已选),则可以在库画板中

看到该文件,并能通过双击该文件在文件编辑器中将其打开。

7.3.4.2 导入实体

选 择 想 要 导 入 的 库 右 击 , 在 弹 出 菜 单 中 选 择 Import , 或 是 在 菜 单 栏 中 选 择

Entry|Import,弹出选择导入文件对话框,如图 7-22 所示。可以选择一个或多个文件,单

击“打开”按钮,PowerBuilder 将所选择的文本文件进行格式转换,然后重新编译这些对

象 , 将 它 们 存 入 指 定 的 库 。 如 果 选 择 的 导 入 文 件 和 现 存 的 文 件 名 字 相 同 的 话 ,

PowerBuilder 将删除旧实体,然后导入文件。注意:如果由于某种原因,比如断电等,致

使导入文件失败,那么原实体也已被删除。

图 7-22 Select Import Files

第 7 章 使用库管理项目

— 225 —

7.3.5 使用源代码编辑器

在画板中,既可以直观地修改对象,也可以使用源代码编辑器来修改所有对象的源文

件。前面已经介绍过将对象导入和导出,如果进行此操作的目的只是为了修改对象的源代

码,那么使用源代码编辑器更为方便。当然要注意,在还没有十分熟悉源代码的语法和语

义之前,最好不要对其进行修改。

使用源代码编辑器对对象所做的修改在保存该对象后立即生效。也就是说,如果修改

后产生错误,必须在源代码编辑器关闭之前予以更正,否则将导致该对象无法在画板中打

开。

可以用 3 种方法打开源代码编辑器:使用工具栏中的打开对话框;在系统树窗口或库

画板中使用鼠标右键单击所选对象,选择弹出菜单的 Edit Source;使用 Output(输出)窗口

中的弹出菜单。

注意

源代码编辑器和文件编辑器类似,不同的是它不能被单独打开。如果一个对象在画板

中已被打开,则在源编辑器中无法将其再次打开。如图 7-23 所示给出在源代码编辑器中

打开对象的示意图。

图 7-23 源代码编辑器

7.3.6 创建动态库

将应用程序生成可执行文件时,如果其中包含的库太多,exe 文件将会变得十分庞

大。因此,PowerBuilder 提供了一种创建动态库的方法。当应用程序需要访问动态链接库

中的对象时才与其建立连接。这种方法可以使可执行文件尽可能小。

在 PowerBuilder 中,动态库是在库画板中创建的。选中想要创建的库右击,在弹出

菜单中选择 Build Runtime Library 或是选择菜单栏中的 Entry|Library|Build Runtime

Library,系统将弹出如图 7-24 所示的创建动态库对话框。在 Name 中已列出所选的库

名,它将作为新生成的动态库的名称。

如果选中 Machine Code 复选框,系统将生成机器码,此时动态库的后缀为.DLL,否

则后缀为.PBD。

Pow erBuilder9.0 基础应用与系统开发

— 226 —

图 7-24 创建动态库

Trace Information 复选框定义是否在可执行文件运行时同时生成一个跟踪文件。

Error Context Information 复选框定义是否在可执行文件执行出错时显示错误内容。

Build Type 表示用何种方式重新生成对象,它有两个选项,增量方式(Incremental)和

全局方式(Full)。增量方式只重新生成修改过的对象及与其相关联的对象,而全局方式则

重新生成全部对象。

Optimization 表示优化库的方式。它有 3 种选项:Speed(优化速度)、Space(节省空间)

和 No Optimization(不优化)。

如果该动态库中某一个对象使用了资源(位图、图标、指针等),必须为此动态库指定

一个资源文件(当然,也可以单独提供一个资源文件),这样,创建动态库时系统会将该资

源文件包含进去。单击 Browse 按钮可出现资源文件的列表,在其中选择文件,在

Resource File Name 文本框中将出现该文件名称。

应用程序运行时,如果遇到需要资源(如位图等)的地方,PowerBuilder 会先在该可执

行文件中查找,如果没有,则在该应用程序的动态链接库 PBD 中查找,如果还没有,它

将在搜索路径的目录中查找该文件。

单击 OK 按钮,系统开始创建动态库。

7.3.7 打印库内容

在库画板中,可以打印 3 种类型的信息:使用搜索产生的结果、库实体的内容以及库

的目录信息。在本章 7.3.2 中,已经介绍了可以将搜索产生的结果打印出来。在此,将介

绍如何打印后两种信息。

7.3.7.1 生成库实体打印文本

库实体的打印文本包含了当前应用中所选实体的内容信息。可以将其打印出来作为资

料进行保存。

选择想要打印的库实体右击,选择弹出菜单的 Print,或选择菜单栏中的 Entry|Library

Item|Print,系统会弹出如图 7-25 所示的打印选项对话框。

如果想要打印应用程序、菜单、窗口或用户对象,可以在该对话框中选择相应的打印

内容。例如想打印出窗口的属性,只要选中 Properties(属性) 复选框即可。

单击 OK 按钮,PowerBuilder 生成打印文本并送到打印机进行打印。

第 7 章 使用库管理项目

— 227 —

图 7-25 打印选项

7.3.7.2 生成库目录打印文本

库目录的打印文本列出了所选库的所有实体,这些实体以对象的顺序排列,它包括对

象的以下信息:对象名、修改日期和时间、对象的大小、对象的注释。

选择想要打印的库右击,在弹出的菜单中选择 Print Directory,或是选择菜单栏中的

Library|Print Directory,系统会将库的目录送到打印机打印。

在打印之前,应确保打印机已经设置好,否则系统将弹出打印出错警告框。可以选择

菜单栏的 File|Printer Setup,对打印机进行设置。

7.4 利用库进行多人开发

开发一个大的应用程序,需要很多人的协作和努力。这就产生了一个问题,所有的对

象都是放在库里的,那么,当很多人同时修改一个库中的对象时,就可能引起冲突。例如

一个开发人员正在修改一个数据窗口,而另一个开发人员也在同时修改,这时就会导致其

中的一个修改是无效的,因为在保存时,它很可能被另一个修改覆盖掉。

为了控制存取库,就需要进行版本控制。版本控制实际上就是对所要修改的库作备

份,每一个开发人员得到想要修改库的相应副本(称之为检出),在副本上作的修改不会影

响到源库,修改完毕,再用该副本替换源库(称之为检入)。源库通常放在服务器上,而副

本则存放在开发人员自己的机器上。

PowerBuilder 提供了 check-out(检出)和 check-in(检入)工具。使用这个工具,可以进

行简单的检入检出操作。除了该工具,PowerBuilder 还提供了版本控制系统接口(SCC

API—Source Code Control Application Programming Interface),通过它可以连接更多版本控

制系统,在应用程序的开发过程中进行版本控制。与 PowerBuilder 提供的这个工具相比

较,外部版本控制系统通常提供了更多的控制功能,如灾难恢复等。它能对复杂的开发过

程进行管理,跟踪对象的开发过程,以及在必要的时候恢复最近的版本等。

通常,处在版本控制下的实体前都会标有表示其状态的图标,通过状态图标,可以清

晰直观地了解每个实体当前所处的状态。PowerBuilder 提供了 5 种表示状态的标记。

(1) 表示该对象位于本地并且不处在版本控制之下。

(2) 表示该对象处于版本控制中并且没有被他人检出。

Pow erBuilder9.0 基础应用与系统开发

— 228 —

(3) 表示该对象已被当前用户检出。

(4) 表示该对象已被他人检出。

(5) 表示该对象在本地机和服务器上的版本不统一。

7.4.1 PowerBuilder 自带的检入、检出工具

PowerBuilder 提供了一个比较简单的检入检出工具,它不具备更高级的版本控制功

能,它的主要作用是锁定已检出且正在修改的对象,以防止他人对其进行修改。由于它比

较方便,在开发的应用程序规模不大时,可以使用该工具。

无论是使用 check-out(检出)和 check-in(检入)工具,或是外部的版本控制系统,都需

要 用 版 本 控 制 系 统 接 口 进 行 连 接 。 连 接 PowerBuilder 提 供 的 内 部 版 本 控 制 系 统

(PBNative),和连接其他版本控制系统一样,均使用 SCC API。两种版本控制都使用相同

的菜单来进行增加、检入、检出、得到最新版本等操作。但是,对于 PBNative,诸如显

示历史以及其他一些版本控制管理工具,都是不可用的。

假如要修改一个对象,为了避免其他人同时对它进行修改,应该将其检出。当做了检

出操作时,PowerBuilder 会在档案文件中将该对象锁定,以避免他人修改,同时在指定的

路径中创建该对象的一个副本,对其进行编译、重新生成,并在该对象前显示一个已经检

出的标记。

如果一个对象已被检出(状态图标为红色对勾),当其他开发人员试图打开该对象时,

系统将会显示一个提示框,如图 7-26 所示,它提示用户,该对象只能被打开而不能被保

存。也就是说,只能打开被其他开发人员检出的对象,而不可以进行修改。

图 7-26 系统提示

当完成了对检出对象的修改后,应该将其检入。执行检入操作后,系统会用最新修改

过的副本代替被检出的原始对象,并删除该副本。对象检入后,其他用户就可以对其进行

修改或是再将其检出了。

下面简要介绍使用 PB Native 进行检入和检出操作以及其他相关操作的具体步骤。

1. 创建源控制连接描述文件

在使用版本控制系统之前,需要在系统树窗口或是库画板中为工作区创建一个

Source Control Connection Profile(源控制连接描述文件),它用来指定与当前工作区相连的

版本控制系统。下面是创建描述文件的一般步骤。

(1) 在 System Tree(系统树)窗口中选中工作区,在弹出菜单中选择 Properties,再选择

Source|Control 标签页,其界面如图 7-27 所示。

第 7 章 使用库管理项目

— 229 —

图 7-27 创建 Source Control Connection Profile

(2) 在 Source Control system(源控制系统,即版本控制系统 )下拉框中,会显示

None(不使用版本控制系统)、PB Native(Power Builder 自带的版本控制系统)和已安装过的

其他版本控制系统。从中选择想要使用的版本控制系统,在这里选择 PB Native。

(3) 在 User ID 文本框中,输入使用版本控制系统的用户名。

(4) Project 文本框用来输入源控制项目,在此项目中保存检出文件的副本,也就是

说,将检出对象放到 Project 所指定的地方。单击该文本框旁边的按钮来显示版本控制设

置对话框,如图 7-28 所示。其中 User Name 中是刚刚指定的用户名,而 Storage Location

则是检出对象真正存放的位置。可以输入路径或单击旁边的按钮进行选择,如果想进行选

择,系统会弹出浏览文件夹窗口,如图 7-29 所示,选择合适的路径,单击“确定”按

钮,返回设置对话框,再单击 OK 按钮,完成检出路径的设置,返回到如图 7-27 所示的

Properties 对话框。

图 7-28 源管理设置 图 7-29 浏览文件夹窗口

Pow erBuilder9.0 基础应用与系统开发

— 230 —

(5) 在 Local Root Directory 中输入或选择想要进行检出、检入操作对象的位置。单击

该文本框旁边的按钮,系统弹出如图 7-30 所示的对话框。选择合适的文件夹后,单击

OK 按钮,完成路径的设置,返回到如图 7-27 所示的对话框。

图 7-30 浏览文件夹窗口

(6) 单击 Connect 按钮进行连接。连接之后,可以看到所有的对象都做了相应的标

记。

(7) 单击 Advanced 按钮可修改高级选项。

(8) Log All Activity 复选框用来选择是否建立一个跟踪日志文件。

在该对话框中,还可以作如下选择。

· 是否要求在 Check In 对话框中一定输入注释。

· 打开工作区时,是否提示在线或离线工作。

· 是否删除临时对象文件。

· 与服务器同步的状态刷新时间。

最后,单击 OK 按钮,完成源控制连接文件的创建。

源控制连接文件创建好后,就可以对对象进行版本控制了。

2. 将实体加入版本控制系统

将实体加入版本控制系统分以下几种情况。

(1) 如果选择加入目标,在系统树窗口中单击相应的目标,在弹出菜单中选择 Add to

Source Control,系统将弹出如图 7-31 所示的选择框,提示选择该目标中的多个文件还是

只选择该目标文件本身。如果选择多个文件,系统将会弹出一个列有目标中多个文件的列

表框,如图 7-32 所示。在该列表框中还可以对加入的文件进行选择,单击 OK 按钮,所

选对象前的图标由加号变为绿点。

第 7 章 使用库管理项目

— 231 —

图 7-31 将目标加入版本控制系统中

图 7-32 将多个对象加入版本控制中

(2) 如果选择单个对象,系统将弹出如图 7-33 所示的对话框,此时文件列表中只列出

了所选的单个对象。

图 7-33 将所选单个对象加入版本控制中

注意

使用 PB Native 第 1 次创建好源控制连接文件后,所有对象前的图标都是加号,需要

手动将对象加入版本控制系统中。

在系统树或是库画板中,根据上述情况选中对象,在弹出菜单中,选择 Add to

Source Control(或选择菜单中的 Entry|Source Control|Add to Source Control,但这时只能在

库画板中进行操作,因为库画板中的菜单项对系统树是不起作用的),将对象加入版本控

制系统中,这时对象前的状态图标为绿色圆点。

3. 检出实体

当对象前面的状态图标是绿色圆点时,就可以将其检出。具体操作如下:选中该对

象,在弹出菜单中选择 Check Out,系统将弹出 Check Out 对话框,如图 7-34 所示。

Pow erBuilder9.0 基础应用与系统开发

— 232 —

图 7-34 检出对象

其中文件列表中,列出了所选择的对象、该对象所在的库及其路径。单击 OK 按钮,

该对象被当前用户检出,其状态图标由绿色圆点变为绿色对勾。如果想选择多个对象进行

检入、检出,在库画板的 Tree View 视图中,双击这些对象所在的库,在 List View 视图

中显示出该库对应的所有对象,利用 Ctrl+Click 和 Shift+Click,可以选择多个对象。

注意

如果不再次创建源控制连接描述文件,系统将按照上一次的用户名检出对象,直到在

描述文件中以其他用户名连接为止。

4. 检入实体

当完成了对检出实体的修改后,应该将其检入,以允许其他人对该实体进行修改。操

作方法是,选择想要检入的对象(该对象前面的状态图标为绿色对勾)右击,在弹出菜单中

选择 Check In,系统弹出 Check In(检入)对话框如图 7-35 所示。

图 7-35 检入对象

其中列出了所选择的对象,单击 OK 按钮,该对象被检入,状态图标变为绿色圆点。

也可以选择多个对象同时检入,不同的是 Check In(检入)对话框的文件列表中将列出

所有选择的对象。实际上,当执行 Check In 后,PowerBuilder 所做的工作是用副本中的对

象取代源库中被检出的对象,并改变检出对象的标记,然后将副本删除。

以上介绍的是如何检入及检出,下面简单介绍一下 PB Native 中的其他操作。

5. 清除检出图标

有时候,当将对象检出并修改后,决定放弃更新,这时不能进行检入,以免覆盖掉源

对象。此时只需要清除源对象的检出图标即可。而 PowerBuilder 所做的工作仅是删除对

第 7 章 使用库管理项目

— 233 —

象副本,改变检出对象的标记,并不用副本对象去代替源对象。

操作方法是选中已检出的对象,选择菜单栏中的 Entry|Source Control|Undo Check

Out,或单击右键,在弹出菜单中选择 Undo Check Out,这时系统会弹出一个 Undo Check

Out 对话框,让用户确认,单击 OK 按钮,系统将取消检出,该对象的状态图标变为绿色

圆点。

6. 从版本控制中移去对象

为了对对象进行版本控制,可以将该对象加入版本控制系统中。当不再需要对该对象

进行版本控制时,还可以将该对象移出。这里要注意的是:当前正在被检出的对象是不能

从版本控制系统中移去的。

在库画板中选择想要移出的对象(只有状态图标为绿色圆点的对象才可被移出),在菜

单中选择 Entry|Source Control|Remove From Source Control,系统将弹出如图 7-36 所示的

对话框,其中列出了所选的对象。如果选择了多个对象,则图 7-36 的对话框中将列出所

有所选对象。注意:在库或工作区中,至少有一个对象的状态图标为绿色圆点时,移去对

象的菜单才有效。

图 7-36 从版本控制中移出对象

7. 刷新状态图标

对象的状态图标表明了对象当前所处的状态。PowerBuilder 中,状态图标是存放于计

算机内存中的,它并不会自动被更新,必须设置其刷新时间(见图 7-27)。但有时会出现本

地副本和服务器上的对象不同步的情况,就必须强制进行状态刷新。

在库画板中,选中想要刷新的对象,选择菜单中的 Entry|Source Control|Refresh

Status,系统弹出刷新对象对话框,如图 7-37 所示,其中列出了所选择的对象。

图 7-37 刷新状态

Pow erBuilder9.0 基础应用与系统开发

— 234 —

单击 OK 按钮,状态图标被刷新。刷新后可以确保该对象的副本与源对象的状态一

致。

8. 得到最新版本

为了得到服务器上版本控制中对象的最新版本,除了采用检出操作外,还可以使用得

到最新版本(Get Latest Version)操作来实现。注意使用该功能并不会在服务器上的版本控

制中锁定对象,因此,当一个开发人员得到对象的最新版本之后,其他人依然可以对该对

象进行修改。

在 System Tree(系统树)窗口或库画板中选中对象,在弹出菜单中选择 Get Latest

Version,或在菜单中选择 Entry|Source Control|Get Latest Version,系统将弹出得到最新版

本对话框,其中列出了所选择的对象(如果选择了工作区,则该对话框中将列出工作区中

所有对象),选中对象前的复选框,单击 OK 按钮,就可以得到对象的最新版本。

9. 比较对象

PowerBuilder SCC API 提供了比较对象的功能。利用该功能,可以将副本中的对象与

版本控制中的对象进行比较,通过比较,可以了解副本中的对象和版本控制中对象之间的

改动变化。

PowerBuilder 中没有可视化的差异比较工具,但它允许选择已安装的工具。在此,选

择 Windiff(MicroSoft Visual Studio Tool 中的可视化差异比较工具)。

在创建好源连接描述文件(参见图 7-27)后,选择 Advanced 按钮(这时版本控制系统应

该选择 PB Native),系统将弹出如图 7-38 所示的 PB Native Option 对话框,在该对话框的

文本框中输入差异比较工具的路径,或利用 Browse 按钮进行选择。注意:在文件名后必

须加上%s%s,且前后用空格分隔,即:文件名%s %s。单击 OK 按钮,完成设置。

图 7-38 选择比较差异工具

在安装好可视化的差异比较工具后,右键单击系统树窗口或库画板中的对象,在弹出

菜单中选择 Show Differences,或使用菜单项的 Entry|Source Control|Show Differences,系

统将会弹出如图 7-39 所示的比较差异的窗口。在该窗口中,记录了对象修改前后的差别

以及修改处的行数。关于如何使用差异比较工具,请参考所安装工具的帮助。

以上介绍了 PowerBuilder 中自带的版本控制系统 PB Native,下面将讲述如何使用

SCC API 连接的其他版本控制系统。

第 7 章 使用库管理项目

— 235 —

图 7-39 比较差异的窗口

7.4.2 使用版本控制系统

当开发一个较大的应用程序时,PowerBuilder 中提供的版本控制系统 PB Native 就有

些力不从心了,因此必须自己安装专门的版本控制系统。在此假定使用 VSS(Visual

SourceSafe,它是 MicroSoft Visual Studio 的成员对象之一)。安装一个版本控制系统包

括:安装版本控制提供商提供的客户端软件(安装方法可以参考其手册),使用 SCC API 与

版本控制系统进行连接。

在与版本控制接口相连后,就可以使用版本控制系统。版本控制系统跟踪并存储了应

用程序开发过程的每一次历史修改,也被称作源代码控制系统。在 PowerBuilder 中,源

就是存储在库中的所有对象的集合,因此,版本控制系统发挥的作用是管理所有对象并进

行记录。所谓版本,就是每次将对象检出并进行修改后,为其加上的表示它已被修改的标

示。换句话说,它和该对象的检出次数有关,检出一次,其版本数就增高一点。比如说,

将一个版本号为 1.0 的对象检出并进行修改,当将其检入时,它的版本号就可能变为

1.1。

为外部版本控制系统创建连接描述文件的步骤与 PB Native 相似,除了在图 7-27 所示

对话框的 Source Control Systems 下拉框中选择 MicroSoft Visual SourceSafe,在 User ID 和

Project 中填入在 VSS 已定义好的内容外,其他选项与采用 PB Native 时的设置相同。

VSS 除了具有 PB Native 的 Check Out(检出)、Check In(检入)、Get Latest Version(得

到最新版本)、Show Differences(比较不同)等功能外,还具有 Show History(显示历史记录)

及其他更高级的功能选项。由于不同的版本控制系统具有不同的功能,在此仅简要介绍一

般工具均具有的显示历史记录的功能的运用。

显示历史记录功能用于显示所选对象的所有操作的历史信息。具体操作如下所述。

选中对象右击,在弹出菜单中选择 Show History,或是使用菜单中的 Entry|Source

Control|Show History,系统将弹出如图 7-40 所示的显示历史对话框。在文件列表中列出

了所选的对象,单击 OK 按钮,将弹出历史选项对话框,如图 7-41 所示。

Pow erBuilder9.0 基础应用与系统开发

— 236 —

图 7-40 显示历史对话框

图 7-41 历史选项对话框

在 From、To 文本框中填入限制信息,该信息可以是 Date、Time、Version 以及

Label,在 User 文本框中填入用户名,单击 OK 按钮,出现图 7-42 所示的窗口,该窗口描

述了图 7-40 所选对象的历史记录。关于该窗口的使用,请参考 VSS 中的相关帮助,这里

不再详述。

图 7-42 显示历史窗口

7.5 库管理的应用实例

由于使用库管理项目涉及的内容相对较少,掌握起来比较容易,所以在此仅以创建一

个应用程序为例,说明库画板的应用。

首先进入 PowerBuilder 开发环境,新建一个工作区 Exawspace,操作界面如图 7-43

所示。

第 7 章 使用库管理项目

— 237 —

图 7-43 建立工作区的主界面

再新建一个应用程序对象 appexap,并选择库搜索路径为 D:\PBExap\appexap.pbl,目

标存取路径为 D:\PBExap\appexap.pbt。具体输入内容如图 7-44 所示。

图 7-44 库项目管理实例

操作结果得到对象的树状列表如图 7-45 所示。

图 7-45 操作结果界面

再新建 3 个窗口对象,分别为 w_win1,w_win2,w_win3。在保存窗口时选择对象所

保存到的目标库为 D:\PBExap\appexap.pbl。保存窗口提示框如图 7-46 所示。

Pow erBuilder9.0 基础应用与系统开发

— 238 —

图 7-46 保存窗口提示框

通过以上过程使初始建立的库结构多了 3 个窗口对象,得到对象的树状列表如图 7-

47 所示。

图 7-47 库结构

7.6 练习题

(1) 如何设置库的搜索路径?练习设置和修改库的搜索路径,并在相应路径下检查库

的存在。

(2) 要建立一个具有良好组织结构的库,需要遵循哪些原则?

(3) 在库画板中如何对实体进行导入和导出?练习对数据窗口、菜单窗口和源代码进

行导入和导出操作,并体会导入和导出操作的作用和对应用程序运行的影响。

(4) 练习在库画板中创建一个动态库。

(5) 什么是库的检入、检出?在什么情况下需要用到库的检入、检出功能?试以不同

用户的身份对同一库进行访问和修改,体会设置库的检入、检出功能的作用和应用方法。

(6) 试创建一个应用程序,并对其中的对象进行管理。

第 8 章 数 据 管 道

在进行管理信息系统的开发时,用户们经常需要把数据或数据库结构从一个数据库转

移到另一个数据库。例如,在开发的过程中,需要把表及其数据从测试数据库拷贝到开发

数据库,或从服务器数据库拷贝到本地数据库。在 PowerBuilder 中,提供了数据转换工

具��数据管道来完成以上的工作。

8.1 概述

8.1.1 数据管道的功能

通过 Data Pipeline(数据管道),应用程序能够在不同的数据库表之间转移数据,也就

是说,可以把一个或多个源表中的数据复制到新表或已存在的目标表中。复制的方式根据

应用程序的需要而定,可以删除目标表及其数据后重建目标表,也可以只把最新数据传送

到目标表中。而且,上述的数据转移可以在同一个数据库的不同表之间进行,也能够在同

一个数据库管理系统的不同数据库之间进行。当然,需要时也完全可以在不同数据库管理

系统的不同数据库之间进行。除了转移一般数据(比如数值型、字符型等)外,数据管道还

可以在数据库之间转移图像、声音之类的大二进制对象(Blob 型数据)。 通常,使用数据管道完成以下功能。 (1) 把整个表一次性地复制到另一个数据库中。 (2) 把一个或多个表中的数据灌入到相同 DBMS 或不同 DBMS 的一个表中。 (3) 创建一个与原表有相同结构,但不包含数据的表,即复制表结构。 (4) 修改原表的表结构,但保留原表的数据。 (5) 将网络数据库服务器上的数据复制到本地的 SQL Anywhere 数据库中,这样用户

就可以使用客户机本地的数据库而无须每次都使用网络。

8.1.2 数据管道的使用方法

在使用数据管道时,一般使用以下两种方法来实现数据的转移功能。 (1) 直接通过数据管道画板创建数据管道对象并执行。 (2) 通过数据管道画板创建数据管道对象并保存,然后在程序中通过代码执行数据管

道。 在创建数据管道对象时,必须指定数据管道的如下特性。 (1) Source database(源数据库)。 (2) Destination database(目标数据库)。 (3) Source Table(要从中复制数据的源表)。 (4) Destination table(要存放数据的目标表)。

Pow erBuilder9.0 基础应用与系统开发

— 240 —

(5) Pipeline operation(要执行的数据管道操作类型)。

(6) Commit(运行数据管道时事务提交的频率)。

(7) Max Errors(容许出现的最多错误数)。

(8) Extended Attributes(是否要把表的扩展属性一起传送到目的数据库中)。

8.2 创建数据管道对象

8.2.1 建立数据管道的步骤

在数据管道画板中建立一个数据管道对象,分为以下几个步骤。

(1) PowerBuilder 的菜单 File|New...,或单击工具栏上的 按钮,打开 New 对话框。

(2) 在打开的对话框中,选择 DataBase 标签页,如图 8-1 所示。

图 8-1 新建数据管道

(3) 在对话框中的 Target 下拉框中选择合适的目标库,单击 OK 按钮,弹出 New Data

Pipeline 对话框,如图 8-2 所示。

图 8-2 New Data Pipeline 对话框

第 8 章 数据管道

— 241 —

在这个对话框中,用户必须选择数据管道对象的数据源以及进行数据转移的源数据库

和目标数据库。有 4 种可选择的数据源:Quick Select,SQL Select,Query,Stored Procedure,

它们决定了生成数据管道时所需的 SQL 语句的 4 种不同方法。这 4 种方法与本书的第 6

章中创建数据窗口的方法是完全一样的。

选择了数据源之后,再选择数据转移的源数据库与目标数据库。如果已经在数据库画

板中连接了数据库,那么该数据库将是默认的源数据库。如果所需的数据库没有在源数据

库与目标数据库的列表框中列出,那么必须使用数据库画板定义相应的 DataBase

Profile(数据库描述文件)。

在这里,为了具体说明如何在不同的数据库之间转移数据,源数据库选择了局域网服

务器上的 SQL Server(mycjxy),目标数据库选择了本地的 Access 数据库(myaccess),数据

源为 Quick Select,然后单击 OK 按钮。

(4) 在打开的 Quick Select 对话框中选择“学生表”,把其所有的列都选中,然后单击

OK 按钮,便进入 Data Pipeline Painter(数据管道画板)工作区,如图 8-3 所示。

数据管道操作工具条 数据管道选项区

源表数据结构区 目标表数据结构区

图 8-3 数据管道画板工作区

(5) 如果只用数据管道来简单地把一个表从 SQL Server 数据库转移到 Access 数据

库,那么,现在就可以按下数据管道操作工具栏上的 “执行”按钮,完成数据转移。

打开 Access 数据库,可以看到“学生表”已经从 SQL Server 库中转移过来。其实,这是

数据管道的一个最简单的例子。

(6) 如果想保存数据管道,可以按下工具栏上的 Save 按钮,指定保存在那个库中,

然后为新的数据管道命名(默认的数据管道命名一般以 d_开头)。

8.2.2 修改数据管道

当定义完一个数据管道之后,还可以以各种方式修改它。下面,利用数据管道画板来

逐一介绍如何修改数据管道。

Pow erBuilder9.0 基础应用与系统开发

— 242 —

8.2.2.1 修改数据管道选项区

在这里,可以修改:要存放数据的目标表,要执行的数据管道操作类型,运行数据管

道时事务提交的频率,容许出现的最多错误数,是否要把表的扩展属性一起传送到目的数

据库中等。以上修改操作的详细解释见表 8-1。

表 8-1 数据管道选项区属性表

选 项 说 明 选项值 默认值

表(Table) 目标表的名称 必须用户输入 如果源表与目标表不一样,默认表名为

源表的表名;如果一样,目标表名为源

表表名后加“_copy”

选项(Option) 数据管道操作类型

Create - Add Table Replace - Drop/Add Table Refresh - Delete/Insert Rows Append - Insert Rows Update - Update/Insert Rows

Create - Add Table

提交(Commit) 以多少行一次提交事务 None , All , 1 , 10 , 100 , 1000 ,

10000,100000 100

键(Key) 目标表的主键名称 必须用户输入 如果源表只有一个,键名为表名后加

“_x”

最 大 错 误 数(Max Errors)

在数据管道结束前,允

许的最大错误数 No Limit , 1 , 10 , 100 ,

200,。。。,900,1000 100

扩 展 属 性(Extended Attibutes)

是否把所选源表中列的

扩展属性传送到目标数

据的系统表中

选中,不选中 这一选项只对 Create 和 Replace 有效,

当选中 Refresh, Append, Update 时这一

选项不显示

不选中

表 8-2 是选择 5 种数据管道选项(Option)时,不同的选项使数据转移采取了不同方

式。

表 8-2 数据管道操作表

选 项 对源数据库的影响

Create - Add Table数据管道将在目标数据库中创建指定的目标表。如果目标数据库中已经存在了同名的表,数据

管道运行时会提醒用户同名表已经存在,必须重命名表名。如果不存在同名的表,数据管道运

行时就会创建一个新表并把数据装入该表中

Replace - Drop/Add Table数据管道将在目的数据库中创建指定的目标表。与 Create - Add Tabl�方式的不同点在于,当目

标数据库中已经存在同名表时,Replace - Drop/Add Table 方式将首先删除该表,然后重新创建

Refresh - Delete/Insert Rows 数据管道将删除目标数据库中指定目标表中的所有数据,然后再插入从源表选择的数据。这种

方式要求目标表已经存在,目标表不存在时,数据管道操作将失败

Append - Insert Rows 目标表中原有数据被保留,然后再插入从源表选择的数据

Update - Update/Insert Rows

数据管道对源表中键值与目标表中键值匹配的行生成 SQL UPDATE 语句,由该语句修改目标

表中相应行;如果键值不匹配则生成 SQL INSERT 语句,将相应行插入到目标表中。选择此操

作方式后,目标表中惟一可以修改的是 Key 栏, Key 栏中必须指定主键列,这些列必须惟一

标识目标表中的每一行

8.2.2.2 修改源表数据结构区

源表数据结构区显示了将要进行数据转移的数据库表的信息,它包含了 Source

Name(源列名)、Source Type(源列数据属性)两列,它们是以灰色显示的,表示在此窗口中

是不能修改的。但由于某种原因也可改变数据管道对象定义中的源数据库。

修改源数据库表的列的方法是单击数据管道画板工具栏上的 按钮(Data Source)或从

第 8 章 数据管道

— 243 —

菜单栏的 Design 中选择 Edit Data Source 菜单项,PowerBuilder 将返回到数据源定义窗

口,然后根据需要修改数据源,修改之后,重新返回到数据管道画板工作区。

8.2.2.3 修改目标表数据结构区

目标表数据结构区显示了目标表的列和键值信息,这些列信息只有在数据管道的操作

方式为 Create(创建)和 Replace(替换)时才可编辑。表 8-3 对这些信息进行了详细地解释。

表 8-3 目标表数据结构区属性表

项 目 说 明 值 Destination Name 目标表列的列名 默认值为源列名 Type 列的数据类型 如果 DBMS 没有变,仍为源列的数据类型 Key 确定一列是否为一键列 如果源表中只有一个表且选中所有键列,则默认键为源表列键 Width 列的宽度 默认值为源列宽度 Dec 指定数据精度 默认值为源列数据精度 Nulls 该列是否允许为空 默认值为源列值 Initial Value 该列初始值 如果没初始值,默认为 space(字符型),或 0(数值型) Default Value 该列默认值 默认值由数据库决定

8.2.3 执行数据管道

运行数据管道的方法在创建数据管道时已经提到,在这里要注意,如果已在数据管道

里定义了检索变元,PowerBuilder 就会显示一个对话框让用户输入检索变元的取值,之后

使用该值生成相应的 SQL SELECT 语句来传输检索出来的数据。

在执行数据管道的过程中,读取、写入、出错的行数、执行所费的时间都显示在数据

管道画板窗口的帮助栏中。在数据管道运行过程中,随时可能因数据的完整性等多种原因

而产生错误,PowerBuilder 自动把出错的行显示在数据窗口中,如图 8-4 所示。

图 8-4 数据管道出错窗口

图中的管道错误窗口显示了每个无效的传输行的全部的列,该窗口的最左边一列显示

了出错的信息。如想纠正数据管道在数据转移时产生的错误,应该仔细察看每一条错误信

息,并针对错误进行修改,可以修改出错列的数据(改变其值),然后,更新所修改的数据

(通过单击数据管道操作工具栏的 Update Database 图标来更新),也可以忽略错误。

Pow erBuilder9.0 基础应用与系统开发

— 244 —

如想忽略错误,可选择 Design 菜单的 Design 选项或数据管道工作区上的 Design

图标,进入数据管道定义窗口,修改数据管道的定义。

例如,在执行该数据管道的过程中,出现了如图 8-4 的管道错误。这个管道的源数据

表为 mycjxy 里面的 t_01_zykcglb(专业课程表),目标库为任选一个库。源表有一个字段为

xhl,它惟一标识一条记录(主键),数据类型为 identity int。执行数据管道时的错误信息

为:

SQLSTATE = 23000 [Sybase][ODBC Driver][Adaptive Server Anywhere]

Integrity constraint violation:

column ’xhl’ in table ’T_01_ZYKCGLB_1’ cannot be NULL

通过分析以上错误,发现 xhl 在新表 T_01_ZYKCGLB_1 中也是主键,而在管道定义

时其 Initial Value 为 exclude,从而导致数据管道的错误。要解决这一问题,只要在数据管

道的定义画板中将 Initial Value 的值改为 none 或 0 即可。

8.3 在应用程序中使用数据管道

如果开发一个需在不同表之间转移数据的应用程序,可以在数据管道画板中设计适当

的数据管道对象并保存,然后再编写程序对其加以控制,从而可以通过执行应用程序实现

数据移动。

在应用程序中使用数据管道,一般有以下 5 个步骤。

(1) 创建所需对象。

(2) 执行初始化操作。

(3) 启动数据管道。

(4) 处理行错误。

(5) 执行结束操作。

在第 1 步中,必须创建以下几个对象:用数据管道画板创建数据管道对象;创建一个

继承数据管道的标准类用户对象;创建一个窗口,在窗口上放置用于显示数据管道运行结

果与运行过程中出错信息的数据窗口控件。

执行管道的初始化操作、启动管道、处理错误、执行结束操作,这几个步骤是在程序

中用脚本来实现的。

在本节中,将介绍数据管道用户对象的属性、事件与函数,然后用两个例子来说明怎

样在程序中使用数据管道来实施数据转移。

8.3.1 数据管道用户对象

8.3.1.1 数据管道用户对象属性

数据管道用户对象有以下几个属性,其描述见表 8-4。

第 8 章 数据管道

— 245 —

表 8-4 数据管道用户对象属性表

管道属性 类 型 说 明 ClassDefinition PowerObject 包含管道对象定义信息的类型为 PowerObject 的一个对象 RowsInError Long 数据管道运行过程中发生错误的行数 RowsRead Long 数据管道运行过程中在源数据库表中读取的记录数 RowsWritten Long 数据管道运行过程中当前已经写入目表数据库表的行数

DataObject String 保存数据管道对象名(在数据管道画板中创建的对象),其用法跟数据窗口控件中的

“DataObject”比较接近

Syntax String 包含用于创建数据管道对象的语法,可以通过一些字符串函数(如 Replace()、Mid())对数

据管道定义进行动态修改

8.3.1.2 数据管道用户对象事件

数据管道用户对象的事件除了两个标准事件 Constructor 和 Destructor 之外,还有 3 个

特有的事件,它们是 PipeStart、PipeMeter 和 PipeEnd。

Constructor 和 Destructor 事件在数据管道用户对象创建和对象被清除时触发。

PipeStart 事件在开始执行 Start()或 Repair()函数时触发。

PipeMeter 事件在每次读或写一块(block)数据时触发,设计数据管道对象时,定义的

Commit 选项的大小决定了块的大小。该事件是数据管道对象比较常用的事件,在该事件

中用户可以把数据管道的运行状态显示在窗口中。

PipeEnd 事件是在 Start()或 Repair()函数执行结束时触发。

8.3.1.3 数据管道用户对象函数

数据管道用户对象有 9 个函数,其中有 6 个函数跟其他对象的同名函数意义相同,有

3 个是数据管道对象特有的函数,表 8-5 列出 6 个一般的函数,下面详细介绍 3 个数据管

道对象特有的函数。

表 8-5 数据管道用户对象函数表

函数名 返回值 功 能 ClassName String 返回该对象的名称 GetContextService Integer 创建指定服务(包括 ContextInformation、Internet、以及 Keyword)的上下文相关实例 GetParent PowerObject 返回父对象的引用指针 PostEvent Boolean 将某个事件添加到该对象消息队列的尾部 TriggerEvent Integer 触发该对象的指定事件并执行该事件的事件处理程序 TypeOf Object 返回该对象的类型

1. Start()

Start()函数的功能是运行数据管道对象,将数据从源数据库传送到目标数据库。这里

的数据管道对象是从系统对象“pipeline”继承得到的用户对象的一个属性。其语法为:

pipelineobject.Start(sourcetrans,destinationtrans,errordatawindow{,arg1,

arg2,...,argn})

各参数的含义如下。

pipelineobject:将要被执行的数据管道用户对象名。该用户对象包含在数据管道画板

中建立的数据管道对象。

Pow erBuilder9.0 基础应用与系统开发

— 246 —

sourcetrans:连接到源数据库的事务对象名。可以是默认的事务对象 SQLCA,也可

以是应用程序自己创建的事务对象。

destinationtrans:连接到目标数据库的事务对象名。可以是默认的事务对象 SQLCA,

也可以是应用程序自己创建的事务对象。

errordatawindow:用于显示数据管道运行过程中出现错误的数据窗口控件名。程序中

无需把某个数据窗口对象关联到该控件上,系统会根据出现的数据管道错误自动生成所需

的数据窗口对象。

arg1, arg2,..., argn 是可选参数,用于在数据管道运行时所需的检索参数。

该函数执行后将返回一个整型数值,函数执行成功时返回 1,发生错误时返回一个负

值。这些返回值所代表的含义见表 8-6。

表 8-6 start 函数返回值含义表

值 含 义 值 含 义 -1 打开数据管道失败 -9 目标库的 SQL 语句有致命错误 -2 列数太多 -10 超出了最大的错误数 -3 表已经存在 -12 数据库表语法错误 -4 表不存在 -13 没有提供必需的关键字 -5 未建立与数据库的连接 -15 数据管道已经在运行 -6 错误的检索参数 -16 源数据库出错 -7 列不匹配 -17 目标数据库出错 -8 源数据库的 SQL 语句有致命错误 -18 目标数据库是只读的

2. Repair()

Repair()函数在当数据管道的某些行出现错误之后使用。当数据管道发生错误之后,

错误行将显示在 DataWindow 控件中,用户可以利用 Repair()函数对数据库进行修复。其

语法为:

pipelineobject.Repair ( destinationtrans )

其中:

pipelineobject 为管道用户对象名。

destinationtrans 为连接到目标数据库的事务对象的名称。

Repair()函数返回长整型值。函数执行成功时返回 1,发生错误时返回值见表 8-7。

表 8-7 Repair 函数返回值含义表

值 含 义 值 含 义 -5 连接数据库失败 -12 数据库表语法错误 -9 目标库中有致命的 SQL 错误 -15 数据管道已经在运行 -10 超出了最大的错误数 -17 目标数据库中有错误 -11 无效的窗口句柄 -18 目标数据库是只读的

3. Cancel()

Cancel()函数用来终止数据管道的运行,通常是在应用程序运行函数 Start()或 Reparir()

期间调用此函数,其语法为:

pipelineobject.Cancel ( )

第 8 章 数据管道

— 247 —

其中,pipelineobject 表示数据管道用户对象的名称。

Cancel()执行的返回值为长整型,函数执行成功时返回 1,发生错误时返回-1。

注意

只有当应用程序已经调用了管道对象的 Start()或 Repair()函数,并且这些函数还在运

行时,应用程序才能够调用 Cancel()函数来终止管道的继续运行。

8.3.2 在应用程序中创建管道对象

本节通过两个例子来介绍如何在应用程序中创建数据管道对象、使用数据管道,其中

一个例子是在程序中用数据管道来实现 Blob 数据的转移。

8.3.2.1 用数据管道转移一般数据

在这个例子里,利用数据管道把本地服务器上的 SQL Server 数据库“xut_cjxy”中关

于计算机专业本科自考学生的所有学生情况的数据转移到本地数据库“lyxtest”上,数据

转移是通过两种方式进行的:SQL Select(快速查询)与 Stored Procedure(存储过程),如图

8-5 所示是此例子运行时的外观。

图 8-5 数据管道传输一般数据示例

1. 创建管道用户对象

在应用程序中使用数据管道必须创建数据管道用户对象,所以首先创建在这两个例子

中用到的数据管道用户对象。其步骤如下。

(1) 单击 PowerBuilder 工具栏上的 New 按钮,打开 New 对话框,在此对话框中选中

PB Object 标签页的 Standard Class 图标,在对话框底部的 Target 下拉框选择合适的目标,

然后单击 OK 按钮。

(2) PowerBuilder 将会弹出 Select Standard Class Type 对话框,如图 8-6 所示,在该对

Pow erBuilder9.0 基础应用与系统开发

— 248 —

话框中选择 Pipeline,然后单击 OK 按钮。

图 8-6 Select Standard Class Type 对话框

(3) 接着便进入数据管道用户对象设计工作区,根据需要对用户对象进行修改,如定

义函数、变量,编写脚本等。

在本例中,定义了一个数据管道用户对象,然后做以下的工作。

在管道对象的“declare”中声明 3 个实例变量:

//在数据管道用户对象中定义 3 个静态文本实例变量

statictext st_read,st_written,st_errors

在管道对象的“pipemeter()”事件中写入如下代码:

//将数据管道运行过程中当前已经读取的行数放到静态文本 st_read 中

st_read.text = string (rowsread)

//将数据管道运行过程中当前已经写入目标库的行数放到静态文本 st_written 中

st_written.text = string (rowswritten)

//将数据管道运行过程中出错的行数放到静态文本 st_errors 中

st_errors.text = string (rowsinerror)

将数据管道用户对象保存为“p_pipe_wmeter”。

2. 创建数据管道对象

创建数据管道对象的方法在本章的第 2 节里已有介绍,这里不再赘述。在这个例子中

通 过 快 速 查 询 建 立 数 据 管 道 p_create_byyy_zk_sql , 通 过 存 储 过 程 建 立 数 据 管 道

p_create_byyy_zk_sp,然后保存这两个数据管道对象。

3. 创建窗口与数据窗口

新建一个窗口 w_byyy_zk,在窗口中加入 4 个命令按钮 cb_execute、cb_repair、

cb_cancel、cb_close,分别用于数据管道的执行、修复、取消与关闭窗口;加入 4 个静态

文本 st_read、st_written、st_errors、st_time,分别用于记录数据管道读出行数、写入行

数、出错行数、所费的时间;加入两个单选按钮 cb_common、cb_blob,用于选择传输的

是一般数据还是 blob 数据;加入一个下拉列表框 ddlb_1,用于选择传输数据的方式是查

询还是存储过程。

为了显示管道对象的出错行(因为某些原因而不能传输到目标表中的行),窗口中包含

第 8 章 数据管道

— 249 —

了一个显示错误的数据窗口控件 dw_errors,不用给此数据窗口控件连接一个数据窗口对

象,数据管道会在程序运行时提供自己的数据窗口对象。

此外,为了检验源数据库的数据是否转移到了目标数据库中,可在窗口中建立一个数

据窗口控件 dw_result,用以显示转移到目标数据库的数据。接着,便要给 dw_result 创建

一个数据窗口对象。

在创建目标表的数据窗口对象时,有一个问题需要解决:目标数据库中还不存在目标

表(因为只有在程序中执行了数据管道以后,数据才能从源库传输到目标库),那么如何建

立这个表的数据窗口对象呢?

解决这个问题的办法是:先运行已经定义好的数据管道对象,将源库中的表结构传输

到目标库中,而不传输数据(可以在数据管道查询画板的 Where 标签页中定义永不为真的

表达式,如“where 1=2”)。然后在已传输到目标库的表的基础上,建立数据窗口对象。

建好数据窗口对象之后,把目标数据库中通过数据管道传输过来的表删除掉(通过数据库

操作快捷菜单的 Drop Table)。

8.3.2.2 利用数据管道转移 blob 数据

数据管道可以在不同表之间传送 Blob 类型(二进制大对象类型)的数据,比如图像、

声音、Word 文档、Excel 表格等。

在本例中,利用数据管道将 PowerBuilder 自带的系统库 EAS Demo DB V3 IM 的 ole

表(表中包含了 blob 类型的 object 列)传输到 lyxtest 的库中。本例运行时的外观如图 8-7 所

示。

图 8-7 数据管道传输 blob 数据示例

下面介绍使用数据管道实现 blob 数据转移功能的具体步骤。

1. 创建管道用户对象

例子一中已经创建了可重用的管道用户对象,在这里直接引用即可。

Pow erBuilder9.0 基础应用与系统开发

— 250 —

2. 创建数据管道对象

按照本章第 2 节的“建立数据管道的步骤”来创建数据管道对象,但在创建数据管道

的过程中会发现,类型为 blob 的列 object 没有出现在数据管道画板中,因此必须利用数

据管道画板来增加 object 列。其做法如下。

(1) 在数据管道画板中选择菜单项 Design|Database Blob�,系统会弹出如图 8-8 所示

的 Database Binary/Text Large Object 对话框。

图 8-8 Database Binary/Text Large Object 对话框

(2) 在 Table 下拉列表框中选择包含 Blob 列的源表,该表中包含的 Blob 列显示在

Large Binary/Text Column 下拉列表框中,在 Large Binary/Text Column 下拉列表框中通过

单击选择所需的 Blob 列,可以在 Destination Column 编辑框中修改目标表相应的 Blob 列

的名称。 (3) 单击 OK 按钮,便可把数据类型为 blob 的列 object 加到目标表中。 (4) 接着进入数据管道画板,进行其他列的修改。 (5) 保存数据管道,命名为 p_create_ole_blob。

3. 创建窗口与数据窗口

窗口与错误数据窗口在例一中已经创建,不用重建,在这里主要介绍如何创建包含

blob 数据类型列的数据窗口对象。 创建此数据窗口对象跟例一相似,但在把 blob 类型列加进数据窗口时比较麻烦。在

这里将进行详细地介绍。 (1) 通过运行数据管道,将源库里面的表结构传输到目标库中,然后以此为基础建立

数据窗口对象。 (2) 在数据窗口画板中选择菜单项 Insert|Control|OLE Database Blob�,然后在数据窗

口的 Design 子窗口中单击鼠标,PowerBuilder 会弹出 Database Binary/Text Large Object 的

对话框。如图 8-9 所示。

第 8 章 数据管道

— 251 —

图 8-9 blob 列属性设置对话框

(3) 在此对话框中设计 blob 列的属性。各选项的意义如下。

Client Class:指定 OLE 对象的客户类,默认为 DataWindow,将数据窗口作为 OLE

的客户对象。

Table:源库中含 blob 数据列的表。

Large Binary/Text Columns:选择包含 blob 数据类型的列。

Key Clause:为一个有效的 WHERE 语句指定一个主键,这个主键用在 OLE 列的

SELECT 和 UPDATE 语句中。在这里填入“id=:id”。

OLE Class:指定处理该 OLE 对象的类。

Client Name Expression:指定 OLE 对象的客户名表达式。

(4) 将 blob 数据列插入到数据窗口中,然后把此数据窗口保存为 p_create_ole_blob。

8.3.2.3 为程序编写代码

在窗口 w_byyy_zk 的 declare 中声明变量。

p_pipe_wmeter lpipeline_Create //定义数据管道实例变量

在窗口的 open 事件中写入代码:

//连接目标数据库(将要进行数据转移的目标数据库)

SQLCA.DBMS = "ODBC" //数据库类型为"ODBC"

//连接数据库的参数,其中数据库为 lyxtest

SQLCA.DBParm = "connectstring=’dsn=lyxtest;uid=dba;pwd=sql’"

connect using sqlca; //进行数据库连接

rb_common.checked=true

//创建数据管道用户对象

lpipeline_Create = CREATE p_pipe_wmeter

dw_result.settransobject(sqlca)

命令按钮 cb_execute 实现执行数据管道操作,在 clicked 事件中写入代码。

Integer li_RC

Pow erBuilder9.0 基础应用与系统开发

— 252 —

Long ll_starttime, ll_endtime

Transaction ltrans_source //定义源库的事务对象

SetPointer(HourGlass!) //运行时设置光标

This.Enabled = False //将“执行”按钮置为“不可用”

cb_close.Enabled = False //不能关闭窗口

dw_result.Reset() //清空数据窗口

ltrans_source = CREATE Transaction //为源库创建事务对象

if rb_blob.checked then //如果转移的是 blob 数据

//建立与源库的连接,源库为“EAS Demo DB V3 IM”

ltrans_source.DBMS = "ODBC"

ltrans_source.DBParm ="connectstring=’dsn=EAS Demo DB V3

IM;uid=dba;pwd=sql’"

connect; //连接源库

Connect using ltrans_source; //与数据库建立连接

If ltrans_source.sqlcode <> 0 Then //如果连接出错

Messagebox("源数据库连接出错", ltrans_source.sqlerrtext) //提示出错信

Return

End If

//给数据管道用户对象的 3 个变量赋值

lpipeline_Create.st_read = st_read

lpipeline_Create.st_written = st_written

lpipeline_Create.st_errors = st_errors

dw_result.dataobject="d_ole_blob" //给 dw_result 连接数据窗口对象

//给数据管道用户对象指定管道对象

lpipeline_Create.dataobject="p_create_ole_blob"

end if

if rb_common.checked then //如果转移的是一般数据

//建立与源库的连接,源库为“xut_cjxy”

ltrans_source.DBMS = "MSS Microsoft SQL Server 6.x"

ltrans_source.serverName="cjxy" //服务器名称

ltrans_source.Database = "xut_cjxy"

ltrans_source.LogId ="sa"

ltrans_source.LogPass = "cad"

ltrans_source.DBParm = ""

connect;

Connect using ltrans_source;

If ltrans_source.sqlcode <> 0 Then

Messagebox("源数据库连接出错", ltrans_source.sqlerrtext)

Return

End If

lpipeline_Create.st_read = st_read

lpipeline_Create.st_written = st_written

lpipeline_Create.st_errors = st_errors

// 根据下拉列表框的内容选择管道用户对象的数据对象

If ddlb_1.text="存储过程" Then

lpipeline_Create.DataObject = "p_create_byyy_zk_sp"

end if

if ddlb_1.text="查询" then

lpipeline_Create.DataObject = "p_create_byyy_zk_sql"

第 8 章 数据管道

— 253 —

End If

dw_result.dataobject="d_byyy_zk"

end if

// 开始执行数据管道

// 取得数据管道执行所花费的时间

ll_starttime = CPU() //开始执行时的时间

//ltrans_source 为源库,sqlca 为目标库,取得 start 函数返回值

li_RC = lpipeline_Create.Start(ltrans_source, sqlca,dw_errors)

ll_endtime = CPU() //执行结束时的时间

st_time.Text = String((ll_endtime - ll_starttime)/1000,"##0.0")

If li_RC <> 1 Then //如果有错

MessageBox("新建表","错误代码: " + string(li_rc))

End if

Commit; //提交

Disconnect Using ltrans_source; //关闭与源库的连接

DESTROY ltrans_source //清除所创建的管道用户对象

dw_result.Retrieve() //将检索数据显示到显示数据窗口

This.Enabled = True

cb_close.Enabled = True

在“cb_repair”的“clicked”事件中实现数据管道修复:

lpipeline_Create.repair(sqlca) //修复数据管道

在“cb_cancel”的“clicked”事件中实现数据管道终止:

lpipeline_Create.cancel() //终止数据管道

为 实 现 把 转 移 到 目 标 数 据 库 的 表 删 除 , 在 “ Functions ” 中 定 义 一 个 函 数

“drop_table(string tablename) return integer”。

Integer li_NotExist

//根据目标库的“DBMS”确定“表已不存在”的代码

Choose Case Lower(Left(sqlca.DBMS, 3))

Case "odb" // -141 表示表已不存在

li_NotExist = -141

Case "syb"

li_NotExist = 3701

Case "syc"

li_NotExist = 3701

Case "ora"

li_NotExist = 3701

End Choose

sqlca.AutoCommit = True

string sqlsyx

sqlsyx="DROP TABLE "+tablename+""

Execute Immediate :sqlsyx;

If sqlca.SQLCode <> 0 Then

If sqlca.SQLDBCode <> li_NotExist Then

sqlca.AutoCommit = False

MessageBox("错误","不能删除新表。错误为: " + String(sqlca.SQLDBCode)

Pow erBuilder9.0 基础应用与系统开发

— 254 —

+ " - " + sqlca.SQLErrText);

Return sqlca.SQLCode

End If

End If

sqlca.AutoCommit = False

commit;

return 0

数据窗口关闭时,要清除数据管道用户对象,删除所创建的表。

DESTROY lpipeline_Create //清除所创建的数据管道用户对象实例

if rb_common.checked then

drop_table("byyy_zk") //删除所创建的表"byyy_zk"

end if

if rb_blob.checked then

drop_table("ole") //删除表"ole"

end if

8.4 应用实例

数据管道对象能够方便地将一个数据库中的数据表传送到另一个数据库中。在此以将

数据库 Pbdb 中的数据表 student 传送到 EAS Demo DB V9 中为例,说明数据管道的使用

方法。

建立一个数据管道对象 p_pipeline,其数据源采用快速选取的方法,如图 8-10 所

示。选取前面实例中用到的数据表 student 的 Sno 字段作为数据管道的字段,如图 8-11

所示。

图 8-10 选取数据源

第 8 章 数据管道

— 255 —

图 8-11 选取数据表字段

单击工具栏提供的“Execute”工具执行数据管道,打开数据库画板可以看到数据管

道的执行结果,数据库 EAS Demo DB V9 中增加了一个数据表 student,如图 8-12 所示。

图 8-12 新增加的数据表

如果应用程序需要,可在原有的数据管道 p_pipeline 的基础上修改其数据源,使其字

段除了 Sno 之外,再增加两个字段 Sname 和 Sage。操作界面如图 8-13 所示。

图 8-13 改数据源

由于 EAS Demo DB V9 数据库中已经存在一个 student 表,所以必须修改数据管道选

Pow erBuilder9.0 基础应用与系统开发

— 256 —

项的设置,由“Create-Add Table”改为“Replace-Drop/Add Table”,如图 8-14 所示。

图 8-14 修改数据管道的选项

经过这样的修改,可以看到 EAS Demo DB V9 数据库中的 student 表的数据列由原来

的一个字段变为 3 个字段,如图 8-15 所示。

图 8-15 新生成的表结构

8.5 练习题

(1) 数据管道具有哪些功能?

(2) 可使用哪几种方式通过数据管道实现数据的转移功能?

(3) 在 Microsoft SQL Server 数据库中创建一个表 Student(Sno,Sname,Sage,

Sdept),然后将表结构及其中的内容均移植到 PowerBuilder 9.0 自带的 EAS Demo DB V4

数据库中。

(4) 通过编程的方式完成第 3 题的功能。

(5) 如果要在第 3 题的 Student 表结构中增加一个属性 Score,那么如何利用数据管道

对象通过编程的方式实现?

第 9 章 在 Pow erB uilder 9.0 中开发 JS P

本章主要介绍了 JSP 的一些基础知识以及在 PowerBuilder 9.0 下开发 JSP 的基本方法

和步骤。

9.1 JSP 简介

JSP(Java Server Pages)技术提供了一种简单、方便的创建含有静态与动态内容的 Web

页面的方法。JSP 文件是一种基于文本的文档,包含了一些像 HTML 或 XML 的静态标识

和一些用 Java 语言编写的小程序块。JSP 不但继承了所有 Java Servlet API,而且可以使用

所有的 Java API 和组件。

作为 J2EE 构架的一部分,JSP 运行在服务器端作为响应客户端 HTTP 请求的中间

层,是可以调用事务处理服务器上 EJB(Enterprise Java Beans)组件的业务逻辑方法。

PowerBuilder 9.0 支持 JSP1.2 版本和 Java Servlet2.3 版本。用户可以将 JSP 对象部署

到 EAServer 4.1.x 应用服务器及其升级版本,Apache Tomcat 4.0 及其升级版本或其他支持

JSP 对象的服务器版本上。

9.1.1 JSP 的工作方式

JSP 在一个安装于 Web 服务器上的 JSP 引擎(一般称为 JSP 容器)中执行。JSP 引擎从

客户端接收到一个请求并把处理结果传递给客户端,客户端会建立或使用其他对象建立一

个响应。例如,JSP 页面会把这个请求发向一个服务器端,服务器端处理请求并对其作相

应的处理,最终以符合 JSP 页面的模式返回给客户端。

在 PowerBuilder 中,JSP 页面以资源表单形式部署在服务器中。如果资源表单中存在

JSP 页面,JSP 引擎会将其转换成一个 class 文件,这个文件实现了 Servlet 的接口同时将

其存储在服务器 Servlet 容器中。依靠 JSP 引擎,这个转换过程可以在部署初始化到第 1

次接收到请求之间的任何时刻发生。只要 JSP 页面未被修改,以后的请求可以重复使用这

个 Servlet class 文件,从而减少重复请求所需要的时间。

某些 JSP 引擎可以使用一些不同协议控制请求与响应,但是所有的 JSP 引擎都能控制

HTTP 请求和 HTTP 响应。在 Javax.servlet.jsp 包中的 JspPage 和 HttpJspPage 两个 class 文

件定义了编译 JSP 页面的接口,这些接口包含以下 3 种方法。

(1) _jspInit()。

(2) _jspDestroy()。

(3) _jspService(HttpServletRequest request, HttpServletResponse response)。

JSP 文件在执行时先由 JSP 引擎进行语法分析,并生成 Java 源文件(即被转换成

Servlet 文件)。当实例化一个 Servlet 时,JSP 引擎就会调用_jspInit 方法;相反,当内存不

足或 Web 服务终止时,JSP 引擎就会调用_jspDestroy 方法销毁页面。_JspService 方法用

Pow erBuilder9.0 基础应用与系统开发

— 258 —

来处理客户请求并将响应返回到请求的客户端。

9.1.2 JSP 应用程序逻辑及其内容

一个 JSP 页面不但可以包含一些用于显示的静态文本模板,还可以包含一些动态程序

模块。具体来说,一个 JSP 页面主要包含以下几个部分。

(1) 为页面指示全局信息,加入必要的外部文件。

(2) 脚本元素的使用及其执行。

(3) 标准标签与动作元素,其中执行动作元素包括使用 JavaBeans 组件,设置或取得

变量值,下载相关插件或发送相应请求。

(4) 自定义标签库。

JSP 应用程序逻辑可以由 JavaBeans、EJBs、自定义标签库、脚本语句等组件提供。

通过脚本和表达式把这些组件和标签在 JSP 页面中组合起来。JavaBeans、EJBs、自定义

标签库、脚本等详细内容将在后面的 JSP 页面设计一节中作详细的描述。

9.2 在 PowerBuilder 9.0 中使用 JSP 开发向导

与 PowerDynamo Web Site 开发向导相似,使用 JSP Web 开发向导可以创建一个有例

子代码、用于保存文件的文件夹和配置环境的开发对象。JSP 页面被打包并以 WAR 文件

的形式部署在服务器上。

在开发向导中有一个服务配置页面,用户可在此处选择页面中可能用到的变量,除了

部署配置名外的所有变量都可在页面创建之后进行修改。

9.2.1 服务器类型说明

选择 Web 服务器时,开发向导一般会通过详细说明引导用户连接指定的 Web 服务

器。表 9-1 描述了开发向导需配置的服务器属性。

表 9-1 使用 JSP 开发向导配置服务器属性

Web 服务器 需配置的服务器属性

JSP server 服务器属性说明

EAServer EAServer 服务器配置文件与 HTTP 端口选择

Tomcat配置用于部署的文件夹(通常是 Tomcat 安装路径下的 webapps 文件夹),HTTP 服

务器与端口名称、登录名和密码

Command Line 使用 Command Line 可以把 Web 应用程序部署到任何支持 JSP 1.2 的服务器上

9.2.2 常用配置命令

可以利用 Java 控制台工具,如 Apache Ant,完成 Web 应用的部署工作,或直接把应

用对象部署到 EAServer、Tomcat 或其他支持 JSP 1.2 的服务器上,如 IBM WebSphere 和

BEA WebLogic。

命令行部署包括的界面和选项见表 9-2。

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 259 —

表 9-2 常用配置命令

名 称 类 型 作 用 Description 文本框 描述配置情况

Command 命令栏 在定义序列中增加一个或多个命令行;使用方向键可以控制命令行在命令序列

中的前后位置;可以加入宏命令

Start-up directory 命令栏 (可选择)改变当前路径之前在同一个命令栏中执行这个命令,其相当于用 cd 命

令去改变当前路径 Show messages in output window 文本框 在 PowerBuilder Output window 中显示命令行工具(默认选择)

Automatically generate WAR file 文本框 默认情况下配置目标是产生一个 WAR 文件。如果要使用命令来生成 WAR 文

件就要清除这个文本框 Abort execution on error 文本框 一旦发现一个错误,就停止控制台的执行(默认选择) Command time-out 文本框 (可选择)设置一个以秒为单位的超时时间,这适用于你所输入的所有命令行

9.2.3 配置常用宏命令

可以把常用配置编写为宏命令,在 JSP 开发向导中有 5 种宏命令,见表 9-3。

表 9-3 控制台宏命令

宏命令 粘贴文本 宏命令中可加入的内容 Configuration Directory $(ConfigDir) 在以后的开发向导页面中指定当前路径的副本 Display Name $(DisplayName) 从 web.xml 文件中显示当前目标名称,也可以在开发向导中指定目标名称

WAR Filename $(WARFile) 只显示文件名,不显示完整路径。如果在控制台中没有建立 WAR 文件,

则必须在开发向导的相关页面中指定文件名 Build Directory $(BuildDir) 在 JSP 开发向导中指定目标文件夹的完整路径 Source Directory $(SourceDir) 在 JSP 开发向导中指定目标文件的完整路径

9.3 JSP 页面设计

JSP 页面可以使用包括 XML 在内的任何结构化的语言编写,通常情况下使用 HTML

语言。在 PowerBuilder 中,可以使用 Web 页面的新建对话框中的任何页面向导来创建

JSP 页面,并且编辑方式与任何其他 HTML 页面一样。当新建一个 Web 页面时,向导会

使用默认的.jsp 扩展名来取代.htm 扩展名。

9.3.1 JSP 页面设计元素

JSP 页面中除了包括一些专门的 JSP 元素之外,还可以加入标准的 HTML 元素、控

制方法、客户端脚本及 ASP 代码。一般 JSP 元素包括:JSP actions、Directives、JSP

scripting elements、Custom tags 和 JSP Web services。

以上几种 JSP 元素将在下文中详细介绍。

9.3.2 页面标识

在 JSP 页面中所有的标准 JSP 行为、代码元素都写在 JSP 页面标识中,脚本语言一般

使用 Java 语言。表 9-4 中列出了一些常用标识。

Pow erBuilder9.0 基础应用与系统开发

— 260 —

表 9-4 用于表示 JSP 元素的标识

图 标 描 述 <%> 服务器端脚本 <%=> 服务器端表达式 <%!> 服务器端申明 <jsp:> 标示指令,例如:<jsp:useBean...> </jsp:> 标示指令结束,例如:</jsp:useBean> <jsp:/> 表示整个指令包括结束和开始标识,例如:<jsp:getProperty... /> <ctl:> 常用标识,例如:<j2ee:action...> </ctl:> 标识结束标志,例如:</j2ee:action> <ctl:/> 表示整个指令包括结束和开始标识,例如:<j2ee:action... /> <?:> 未知标签 <%@ include %> 包含页面指示,例如:<%@ include... %>

9.3.3 JSP 指令

JSP 指令是一种用于执行指令的标签,其所有前缀都是 jsp,一些常见指令标识如

下。

(1) <jsp:useBean>

useBean、getProperty 和 setProperty 指令都用于 JavaBean 组件。useBean 的 id 是所使

用 bean 的名称,并且通过名称可以使用 bean 的 getProperty 与 setProperty 方法。

UseBean 指令用于实例化一个 JavaBean 组件,例如:

<jsp:useBean id="labelLink" scope="session" class="LinkBean.labelLink"

/>

这些 bean 必须使用 Java 语言书写,才能在 Web 应用程序中使用。

(2) <jsp:getProperty>

getProperty 指令用于获得 JavaBean 组件中变量的值,可将其显示在页面中。

(3) <jsp:setProperty>

使用 setProperty 指令可以为 JavaBean 组件中的变量赋值。

<jsp:setProperty name="labelLink" property="url" value="<%=

labelLink.getURL() %>"/>

(4) <jsp:include>

include 指令可以包含一个静态文件或发送请求到一个动态文件,例如:

<jsp:include page="cart.html" flush="true" />

(5) <jsp:forward>

forward 指令可以把客户端的请求发向一个 HTML 文件、JSP 文件或 Servlet 去处理。

(6) <jsp:param>

param 指令可用于在 include 和 forward 指令中描述一个参量,也可用于 params 指令

中,例如:

<jsp:forward page="/jsp/datafiles/ListSort.jsp" >

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 261 —

<jsp:param name="bgColor" value="blue" /> </jsp:forward>

(7) <jsp:params>

params 指令仅用于在 plugin 指令中封装使用 param 描述的 applet 参量。

(8) <jsp:plugin>

plugin 指令可下载一个插件以便通过 Web 浏览器执行一个 applet 或 JavaBean 组件,

通过这种方式可以在页面中产生 HTML 的<embed>或<object>元素。除此之外,也可以通

过 params 与 param 指令描述一个插件所需的参量,并且使用 fallback 指令显示浏览器不

支持的<embed>或<object>元素,例如:

<jsp:plugin type=applet code="Calc.class" codebase="/mathutils" >

<jsp:params>

<jsp:param name="multiplier" value="multipliers/tax.val"/>

</jsp:params>

<jsp:fallback>

<p> unable to start plugin </p>

</jsp:fallback> </jsp:plugin>

(9) <jsp:fallback>

fallback 指令仅用于 plugin 指令中描述用于显示的,而那些浏览器不支持的<embed>

或<object>元素。

9.3.4 在 JSP 页面中加入一条指令

1. 使用菜单中的 JSP Standard Action 加入一条指令

如图 9-1 所示,在菜单 insert 下的 JSP Standard Action 中可选择所要加入的指令。

图 9-1 从菜单中加入所需指令

Pow erBuilder9.0 基础应用与系统开发

— 262 —

2. 使用对话框,设置指令的属性

要设置指令的属性,可以通过指令的属性对话框,如图 9-2 所示。通过对话框可以设

置指令属性值及此属性是否被选择的情况,y 表示需要设置的属性。

图 9-2 指令的属性对话框

9.3.5 添加 applets 和 JavaBeans

使用适当的指令可以把 applets 和 JavaBean 加入到 JSP 页面中。为了在系统目录的组

件列表中显示 applets 和 JavaBean,必须确保将此组件与 WTInfo90.jar 文件包含在 Java 类

文件的路径中。WTInfo90.jar 文件被安装到 Sybase\Shared\We 目录下,默认情况下应该将

其添加到类文件的路径下。

1. 添加 applets

当从组件工具栏中拖动一个 applet 到一个 JSP 页面窗口或代码窗口时,jsp:plugin 属

性对话框显示所选择的 applet 的默认值。当单击 OK 按钮时,该 applet 就通过 jsp:plugin

指令标签被加入到页面中。

当添加 applet 到 JSP 页面时,必须确保该 applet 的 classe 文件可以通过客户端浏览器

访问到。可以使用文件或 HTTP 协议,通过 jsp:plugin 指令的 codebase 属性来指定 classe

文件所在位置。

2. 使用 JavaBean 和 JavaBean 的属性

当从组件工具栏中拖动 JavaBean 到一个 JSP 页面窗口或代码窗口,jsp:useBean 属性

对话框显示所选择的 JavaBean 的默认值。当单击 OK 按钮时,该 JavaBean 就通过

jsp:useBean 指令标签被加入到页面中。

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 263 —

如果此 JavaBean 在一个类文件中,那么这个类文件就会加入到编辑对象的 Web-

Inf\classes 目录中;如果此 JavaBean 在一个存档文件中,那么这个存档文件就会加入到编

辑对象的 Web-Inf\ lib 目录中。

如果 JavaBean 的属性是可读写的,那么组件工具

栏上会列出两个图标,其中一个的属性是可读的,另一

个的属性是可写的。可读属性正常时,显示为一个向上

的黄色图标,可写属性正常时,显示为一个向下的绿色

图标,如图 9-3 所示。

当把一个可读属性的 JavaBean 组件从组件工具栏中加入到 JSP 页面时,属性设置窗

口中的 jsp:getProperty 属性显示为默认值;当把一个可写属性的 JavaBean 组件从组件工具

栏中加入到 JSP 页面时,属性设置窗口中的 jsp:setProperty 属性显示为默认值。

9.3.6 声明

使用声明可以给 JSP 引擎提供全局信息,或包含一个文件或代码块。一个声明以

“<%@”开始,以“%>”结束,其中包含了声明的名称及其属性。

声明有以下 3 种:page、include 和 taglib,具体说明如下。

1. Page 指令

page 指令定义的属性可以作用于整个页面,其属性包括 language、extends、import、

session、buffer 和 errorPage 等。

2. Include 指令

Include 指令可在 JSP 页面中包含一个静态文件,同时解析该文件中的 JSP 语句,下

面的例子是一个 Include 指令和 Include 指令的标准标签。

<%@ include file="header.htm" %>

3. Taglib 指令

Taglib 指令用于用户自定义标签库及其自定义标签的前缀。Taglib 指令声明此 JSP 文

件使用了自定义的标签,同时引用标签库,也指定了它们标签的前缀。

必须在使用自定义标签之前使用 Taglib 指令,而且可以在一个页面中多次使用,但

是前缀只能使用一次。

Taglib 指令的基本用法举例如下:

<%@ taglib uri="http://www.mycorp/printtags" prefix="print" %>

4. 在 PowerBuilder 9.0 中添加声明

在 PowerBuilder 9.0 中添加声明的具体步骤如下。

(1) 右 键 单 击 页 面 窗 口 , 在 菜 单 中 选 择 页 面 属 性 或 右 键 单 击 代 码 窗 口 中 的

<BODY ...>,然后选择菜单中的页面属性。

(2) 在 Page Properties 或 Body Properties 中选择 JSP Directives 菜单,并且选择“新

图 9-3 JavaBean 组件的读写属性

Pow erBuilder9.0 基础应用与系统开发

— 264 —

建”按钮。

(3) 在名称的下拉列表框中选择所需指令,如图 9-4 所示。

图 9-4 选择所需指令

(4) 单击属性值列,然后单击“查看”按钮,在 Page Directive Attributes 对话框中会

显示完整的设置按钮,如图 9-5 所示。

图 9-5 声明的属性设置

9.3.7 JSP 中的程序段

JSP 中的程序段用来操作对象或执行计算的代码,通常有几种:<%显示一个

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 265 —

scriptlet%>、<%显示一个表达式%>和<%显示一个声明%>。

1. Scriptlet

Scriptlet 是一个有效的程序段,该程序段可以包含多个 JSP 语句、方法、变量及表达

式,如<% cart.processRequest(request); %>。

2. 表达式

表达式元素表示的是一个在脚本语言中定义的表达式,运行后自动转化为字符串,然

后在 JSP 文件的显示位置插入此表达式,如 Value="<%= request.getParameter("amount")

%>"。

3. 声明

声明指在 JSP 程序中声明合法的变量和方法,如<%! Connection myconnection; String

mystring; %>。

4. 注释

JSP 文件有以下两种注释。

(1) HTML 注释。这种注释的内容会传给浏览器,如<!-- Copyright(C) 2002 Acme

Software -->。

(2) 隐藏注释。这种注释的内容不会传给浏览器,如<%-- Add new module here --

%>。

5. 在 JSP 页面中添加程序段元素

(1) 打开一个 JSP 文件,选择页面工具栏,然后单击右键选择代码编辑框。

(2) 在弹出菜单中选择 New Script|Server|JSP,可以看到上述的几种程序段标志,如

图 9-6 所示。

图 9-6 添加程序段标志

(3) 选择所需的程序段标志,该标志会自动插入到代码中。

Pow erBuilder9.0 基础应用与系统开发

— 266 —

6. 默认对象

当 JSP 页面处理 request 对象时,默认使用一系列对象,这些对象都有其确定的作用

范围。一些对象可以通过代码创建,在创建对象时可以设定其属性,规定何时创建、何时

消除。表 9-5 列出了一些默认对象及其作用范围。

表 9-5 默认对象及其作用范围

默认对象 描 述 作用范围 Request request 控制 Servlet 的请求 request Response 响应 request 控制 Servlet 的响应请求 Page pageContext 页面上下文 Page Session Session 对象右客户端的请求创建 session application 从 Servlet 的配置可以获得其上下文,如使用 getservletConfig().getContext()方法 application Out 在输出流中写入输出对象 out config JSP 页面的 ServletConfig 实例 config Page 页面类的一个实例,用于处理当前请求,若使用的语言是 Java,他等同于 this page exception 由错误控制页面处理的抛出意外 exception

默认对象显示在系统目录的语言页面工作条中,如图 9-7 所示。

图 9-7 系统目录中的默认对象

使用 Web Target 对象模型封装对象也可达到和默认对象一样的功能。例如,在服务

器端使用 out.println 方法的功能等同于使用 psDocument.Write 方法的功能。

7. 作用范围

在 JSP 应用程序中一个对象可以有 4 种作用范围。

(1) Page:仅在对象所产生的页面有效,当响应返回或发出请求时对象被释放。

(2) Request:在响应请求的对象产生的页面可用,当请求被处理后对象被释放。

(3) Session:在同一个 Session 中都有效,当 Session 结束时对象被释放。

(4) Application:在同一个 Application 中都有效,当运行环境改变 ServletContext 时对

象被释放。

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 267 —

9.3.8 标签

标签也称为扩展标志,增强了 JSP 的功能。标签库定义了实现某一特定功能的一系列

指令,例如控制 SQL 请求。虽然可以使用 PowerBuilder 的向导创建 Web services 使用的

常用标签,但是 PowerBuilder 中使用的标签库必须使用像 PowerJ 这样的工具来建立。

URI 确定一个标签库必须与标签库描述文件和标签控制类联系起来。

1. 标签控制

用于标签控制的文件是一个 Java 类文件,该文件定义了一个指令的语义。JSP 是通

过 实 例 化 这 样 一 个 对 象 来 控 制 页 面 中 的 指 令 的 。 控 制 对 象 继 承 了

Javax.servlet.jsp.tagext.Tag 接口,接口中定义了标签控制的基本方法,包括 doStartTag 和

doEndTag。BodyTag 接口继承了 Tag 接口,通过增加方法来控制标签。

2. 封装标签库

为了将 JSP 页面和标签库联系起来,可以使用 taglib 指令定义 URI 确定一个标签库文

件(TLD)的位置,此 TLD 文件必须部署在 JSP 容器的类路径中,通常情况下该文件被放

在 Web application 的 WEB-INF /tlds 路径下。同时标签库的类文件也必须部署在 JSP 容器

的类路径中,通常情况下该文件被放在 Web application 的 WEB-INF/classes 路径下,或

WEB-INF/lib 路径下的 JAR 文件中。

3. 在 JSP 页面中使用标签库

在 PowerBuilder 中,可以使用系统目录中的组件栏在 JSP 页面中加入标签。可以使

用的标签必须是在 PowerBuilder 的组件栏所显示的标签库中。可以在页面的属性设置框

中加入一个目录或是一个标签库描述文件。当把标签库添加到页面中时,属性框会提示输

入前缀。当从标签库中新添加一个标签时,该前缀作为一个用于查阅标签库的标志。

PowerBuilder 会自动把路径添加到 web.xml 页面对象所属的 TLD 文件中。PowerBuilder

也会把这个标签库添加到对象的部署配置属性的 Tag Libraries 页面中。

9.3.9 错误控制

当执行客户端的请求时,页面或其中的 Java 代码所产生的错误可以由页面捕获,通

过页面中的代码可以控制抛出的异常,这继承了 Java 语言的异常机制。

1. 未捕获的异常

由程序抛出且不能被捕获的异常可以通过一个错误页面来控制,所有客户端请求所产

生和未被捕获的异常,都可以在指定的错误页面中显示出来。当客户端请求使用

putAttribute 方法抛出 javax.servlet.jsp.jspException 异常时,其使用了 javax.ServletRequest

中的 java.lang.Throwable 异常类。

2. 使用错误页面

如果把一个 JSP 页面指定为错误页面,那么可以使用默认对象的变量来获得某个异常

的信息,该异常是 java.lang.Throwable 的一个对象。

Pow erBuilder9.0 基础应用与系统开发

— 268 —

通过设定 erroPage 属性设置错误页面的 URL 可以指定一个错误页面,例如<%@

page errorPage="ErrorPage.jsp" %>。设置页面的 isErrorPage 属性为 true,可以将一个 JSP

页面定义为错误页面,例如<%@ page isErrorPage="true" %>。通过此页面就可以把捕获

的异常显示出来。

9.4 JSP 编程实例

本节主要介绍如何使用 PowerBuilder 开发 JSP 的简单例子。通过一步一步地详细介

绍,引导用户完成 JSP 简单实例。

1. 新建 Workspace

启动 PowerBuilder 9.0,从菜单栏中选择 File|New 选项,在弹出的对话框中选择

Workspace 标签页,选中 Workspace 图标,然后新建一个 workspace。

2. 新建 JSP Target 对象

从菜单栏中选择 File|New 选项,在弹出的对话框中选择 Target 标签页,选中 JSP

Target 图标,并单击 OK 按钮,启动 JSP Target 创建向导,开始创建 JSP Target 对象。如

图 9-8 所示。

图 9-8 选择开发对象

3. 设置 JSP Target 对象存储路径及名称

在文本框中分别输入 JSP Target 对象名称及其存储路径,如图 9-9 所示。

4. 选择部署的 JSP Server

PowerBuilder 9.0 提供了以下 3 种 JSP Server 供用户部署 JSP 应用:EAServer、

Tomcat、CommandLine,这里选择 EAServer 作为 JSP 实例的 JSP Server,如图 9-10 所

示。

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 269 —

图 9-9 设置 JSPTarget

图 9-10 选择 EAServer

5. 设置 JSP Server 配置信息

由于本实例选用 EAServer 作为 JSP 应用部署的 JSP Server,PowerBuilder 会提示选择

有效的 EAServer 配置文件作为服务器连接的基本信息。关于 EAServer 服务器及其配置文

件等详细介绍,请参阅本书第 10 章“使用 Web 服务”。根据本实例要求,这里需要选择

已配置好的配置文件及使用的 HTTP 端口,如图 9-11 所示。

6. 新建 JSP 页面

JSP Target 对象创建成功后,就可以创建 JSP 页面。从菜单栏中选择 File|New 选项,

在弹出的对话框中选择 Web 标签页,然后选中 Web/JSP Page 图标,并单击 OK 按钮,启

动 JSP 页面创建向导。如图 9-12 所示。

Pow erBuilder9.0 基础应用与系统开发

— 270 —

图 9-11 选择配置文件

图 9-12 选择 Web/JSP Page

7. 通过向导设置 JSP 页面属性

在 JSP 页面创建向导的引导下,用户需设置新建的 JSP 页面的相关属性,例如 JSP 页

面的名称、背景及需添加的样式表路径等。本实例 JSP 页面名称为 table.jsp,如图 9-13 所

示。

8. 编写代码

JSP 页面创建成功后,就可以在主界面右侧的工作区中看到一个名为 table.jsp 的编辑

区。在编辑区中,可以直接输入 JSP 和 HTML 代码。如图 9-14 所示。

第 9 章 在 Pow erBuilder9.0 中开发 JSP

— 271 —

图 9-13 设置 JSP 页面

图 9-14 代码编辑框

本实例主要实现 Web 页面中显示系统时间的简单功能,其代码如下:

<HTML>

<HEAD>

<TITLE>table</TITLE>

<%@ page import=�java.util.Calendar�%> <!�导入 util 包中的 Calendar 类--> </HEAD> <BODY bgcolor=�White�> <% Calendar date=Calendar.getInstance(); //实例化一个 Calendar 对象 Out.print(date.getTime()); //调用 Calendar 的 getTime 方法显示系统时间 %> </BODY> </HTML>

9. 部署 JSP 页面

保存 table.jsp 文件,然后右键单击系统目录中的 workspace,在弹出菜单中选择

Pow erBuilder9.0 基础应用与系统开发

— 272 —

“Deploy”,PowerBuilder 自动将刚才创建的 JSP 页面部署到 EAServer 服务器上,同时将

部署信息显示在输出列表框中,如图 9-15 所示。

图 9-15 部署 JSP 页面

10. 浏览 JSP 页面

JSP 页面部署成功后,就可以从客户端浏览器浏览该页面。在地址栏中输入

http://lwj:8080/example/table.jsp,显示的 JSP 页面如图 9-16 所示。

图 9-16 显示结果

到此为止,在 PowerBuilder 9.0 下开发的 JSP 应用程序已经完成。

9.5 练习题

根据本章所学知识,练习创建一个 JSP 页面,能够显示完整的乘法表。

要求:配置 EAServer,使用 PowerBuilder 编程及发布 JSP 页面到 EAServer 服务器。

第 10 章 使用 W eb 服务

本章主要描述了 Web 服务及其相关协议规范,并通过实例详细介绍如何使用

PowerBuilder 9.0 和 EAServer 应用服务器创建 Web 服务及 Web 服务调用客户端。

10.1 Web 服务概述

Web 服务发展至今已经取得了巨大的发展,许多软件公司纷纷宣布对 Web 服务的支

持和应用,并且对 Web 服务进行了很大的改进。Web 服务实际上是对 Internet 应用的一个

延伸,是现有 Internet 应用面向更好的互操作能力的一个延伸。传统的 Web 应用在于如何

让人使用 Web 应用所提供的服务,而 Web 服务则是要解决如何让计算机系统来使用 Web

应用所提供的服务。Web 服务也可以看成是为 Web 应用提供基础服务,可以自由部署在

Internet 上,通过 Web 服务技术实施相互之间的访问的一种技术。

Web 服务构建于现有 r Internet 环境基础之上,使用 HTTP、SMTP、HTTPS 等网络传

输协议作为传输媒体,通过 Web 服务描述语言(WSDL)对 Web 服务自身进行描述。Web

服务采用了基于 XML 的简单对象协议(SOAP)的消息传送机制来实现 Web 服务的互操

作,并通过统一描述、发现和集成协议(UDDI)注册和发布 Web 服务,以允许其他 Web 应

用查询和使用该 Web 服务所提供的服务。

Web 服务的主要目标是在现有各种异构平台上构筑通用的平台及与其语言无关的技

术层,各种平台上的应用依靠这个技术层实现彼此的连接和集成,从而实现更复杂的分布

式商务交易。

10.1.1 Web 服务体系框架

Web 服务技术是为解决在 Internet 环境中,松散耦合的 Web 服务之间进行的互相调

用、互相集成而设计的技术框架。该框架以 XML、SOAP、WSDL、UDDI 协议为技术主

干,根据 Web 服务功能划分各角色,其典型体系架构如图 10-1 所示。

服务注册

中心

服务请求者 服务提供者

绑定

查找

wsdl、uddi

发布

wsdl、uddi

服务描述

服务

服务描述

图 10-1 Web 服务体系架构

Pow erBuilder9.0 基础应用与系统开发

— 274 —

该框架包含 3 种角色:服务提供者、服务注册中心和服务请求者。服务提供者是

Web 服务的供应商,为其他服务和用户提供服务功能。服务注册中心提供服务提供者及

服务的注册,并提供服务检索功能。服务请求者是 Web 服务功能的使用者,可以利用

Web 服务注册中心查找所需的服务。这 3 种角色是根据逻辑关系进行划分的,在实际应

用中,可能会出现角色交叉或互换,即某个 Web 服务既是 Web 服务提供者,也是 Web 服

务的请求者。

Web 服务体系的典型应用过程如下:服务提供者开发一个可以在线访问的服务,然

后将服务的描述信息注册到服务注册中心或发送给服务请求者;服务请求者通过在本地或

服务注册中心查找相关服务,然后通过绑定就可以使用该项服务,为服务使用者提供数据

和业务。

10.1.2 Web 服务的特点

由于 Web 服务是以 XML 等开放的 Web 规范技术为基础的部署在 Web 上的对象,所

以具有比其他任何对象技术都好的开放性。其主要特点如下。

(1) 封装性:Web 服务是部署于 Web 上的对象,具备对象组件的良好封装性。对于

服务使用者,只需了解服务调用接口,而不必考虑服务实现细节。

(2) 松散耦合性:使用 Web 服务可以实现平台的细节与业务调用程序无关,提供松散

耦合的分布式环境。Web 服务利用其声明的 API 函数和调用机制进行访问,对于调用者

是透明的。这使当 Web 服务的实现发生变化,即逻辑编码或平台的实现发生改变,只要

调用接口不改变,客户端无需改变编码。

(3) 规范性:相对于一般对象来说,Web 服务采用开放的标准协议规范(如 UDDI、

WSDL、XML、SOAP 等)进行服务的注册、发布、描述、传输及信息交换,易于机器理

解,为 Web 服务之间的互操作提供了方便。

(4) 集成性:Web 服务采取简单的、易理解的 Web 协议标准作为组件界面描述和协

同描述规范,完全屏蔽了不同软件平台及编程语言的差异,无论是 CORBA,DCOM 还是

EJB 组件都可以通过这一标准的协议进行互操作,实现在当前环境下最高的可集成性。

10.1.3 Web 服务核心技术简介

1. 简单对象协议 SOAP

SOAP 是一种基于 XML 的不依赖于传输协议的表示层协议,用来在应用程序之间以

对象的形式交换数据。SOAP 消息可以借助 HTTP、FTP、SMTP、POP3 等通用开放的

Internet 的网络传输协议传输,为松散的分布式环境中对等地交换结构化和类型化的信息

提供了一个简单的轻量级机制,并且由于 XML 的开放性消息通信模式,SOAP 具有平台

无关性及自我描述和可扩展的优点。SOAP 由以下 3 部分组成。

(1) SOAP 封装结构:定义了一个全面的框架,用于表达消息的内容、消息的处理者

及消息的处理方式。

(2) SOAP 编码规则:定义了一个序列化机制,用于交换应用程序定义的数据类型实

例。

第 10 章 使用 W eb 服务

— 275 —

(3) SOAP RPC 表示:定义了一个协议,用于表示远程过程的调用及响应。

除此之外,SOAP 规范还定义了两个协议绑定,描述了在有或没有 HTTP 扩展框架的

情况下,SOAP 消息如何包含在 HTTP 消息中传送。

2. Web 服务描述语言 WSDL

WSDL 是用来描述网络服务或端点的一种 XML 应用,用于描述 Web 服务及调用方

式,主要从以下几点对 Web 服务的描述提供说明。

(1) Web 服务识别数据类型。

(2) 消息格式。

(3) Web 服务交换方法(如请求/响应、单向、广播等)。

(4) Web 服务位置。

(5) 错误信息及标头信息。

Web 服务调用客户端可以通过 WSDL 文档中描述的 Web 服务的访问操作、使用的请

求/响应消息机制及一组相关服务访问点,通过将其绑定到具体的传输协议和消息格式,

实现对最终部署的服务访问点的访问。

一个完整的 WSDL 服务是由一个服务接口和一个服务实现文档组成。服务接口表示

服务可重新定义,用于在 UDDI 注册中心作为 tModel 发布。服务实现文档描述服务的实

例,每个实例都使用一个 WSDL 服务元素定义,文档中的每个服务元素定义都用于发布

UDDI 商业服务。

在 WSDL 框架中可以对 WSDL 进行扩展,使用任意的消息格式和网络协议均可以对

服务访问点及其使用的消息格式进行描述,WSDL 规范已经定义了 SOAP 消息格式、

HTTP GET/POST 消息格式及 MIME 格式来完成 Web 服务的交互。

3. 统一描述、发现和集成协议 UDDI

统一描述、发现和集成协议(UDDI)是一套基于 Web 的、分布式的、为 Web 服务提供

信息注册中心的实现标准规范,同时也包含一组使企业注册其自身提供的 Web 服务以便

其他企业可以发现此访问协议的实现标准。

UDDI 的核心组件是 UDDI 商业注册,它使用一个 XML 文档来描述企业及其提供的

Web 服务。从概念上来说,UDDI 商业注册所提供的信息包含 3 个部分:White Page(白页)

包括了地址、联系方法和已知的企业标识;Yellow page(黄页)包括了基于标准分类法的行

业类别;Green Page(绿页)包括了关于企业所提供的 Web 服务的技术信息,其形式可能是

一些指向文件或是 URL 的指针,而这些文件或 URL 是为服务发现机制服务的。

所有的 UDDI 商业注册信息存储在 UDDI 商业注册中心中。企业可以通过 UDDI 商业

注册中心的 Web 界面将注册信息加入到 UDDI 的商业注册中心。UDDI 商业注册中心在

逻辑上是集中的,在物理上是分布式的,由多个根节点组成,相互之间按一定规则进行数

据同步。当一个企业在 UDDI 商业注册中心的一个实例中注册后,其注册信息会被自动复

制到其他 UDDI 根节点,所有希望引用该企业的 Web 服务的应用均可以发现、查找并引

用。

总之,Web 服务的出现,极大促进了电子商务及其他各种基于 Web 应用的发展,为

企业应用集成(EAI)提供了一种技术实现方式,它标志着第 3 次计算机技术的革命,是下

Pow erBuilder9.0 基础应用与系统开发

— 276 —

一代分布式计算的核心。

10.2 Web Services 的创建和配置

Web 服务是下一代分布式组件模型,是综合技术的一种自然扩展。Web Services 能够

使各种服务在公网上自由通信,也包括创建一个面向服务的计算方法。Sybase 提供了一

个全面的 Web 服务框架,可以实现 Web 服务的开发、发布、访问、编程和综合管理功

能。

在 PowerBuilder 开发环境下并不能直接创建 Web 服务,需要支持 Web 服务开发的应

用服务器作为辅助,应用服务器可以为 Web 服务的创建和应用提供完整的环境。Sybase

公司的 EAServer 应用服务器 4.2 以上版本充分支持目前的 Web 服务标准,能使企业快速

地将商业功能配置为 Web 服务,使 Web 服务切实、方便地包含到企业应用开发框架中,

从而降低了企业应用费用,减少了企业应用开发周期。

本节通过实例介绍如何使用 PowerBuilder、EAServer 服务器及其 Web Services 开发工

具(WST)创建 Web 服务。

10.2.1 EAServer 及其 WST 简介

1. EAServer 简介

EAServer 应用服务器不仅对客户会话、安全、线程、数据库连接和事务流等提供了

高效的管理工具,而且提供了完整的开发工具,简化了创建和管理 Internet 应用的过程。

同时 EAServer 也为基于组件的分布式应用提供了中间层的逻辑开发框架,允许开发人员

方便地开发并部署 PowerBuilder、EJB、ActiveX 及 C/C++组件,而不必要求掌握组件开

发的专业知识。EAServer 的可升级性和平台独立性还允许用户在低廉的单处理机上进行

开发,并应用在企业级包含多处理器的服务器上。

从 EAServer4.2 开始,Sybase 公司将 Web 开发与 EAServer 服务器集成在一起,允许

开发人员迅速创建并部署 Web 服务应用。

EAServer 的 Web 服务体系结构包括以下组件。

(1) 封装在 EAServer 内的基本 SOAP 引擎,支持 SOAP1.1 标准。

(2) 创建和管理 Web Services 的工具,包括如下。

· 基于 Web 的 Web 服务控制台,用于管理、监控、应用 Web 服务。

· 基于 Web 的 UDDI 控制台,用于 UDDI 管理、Web 服务发布/解除发布、浏览

UDDI 注册中心。

· 图形化的 Eclipse 插件,用于设计、开发并将 Web 服务应用到 EAServer 环境中,

提供管理控制运行在 EAServer 服务器上的 Web 服务,监视 Web 服务的输入及输出消息

流,创建独立的 Java 及 JSP 测试的 Web 服务调用客户端,及 Web 服务的发布和查询等功

能。

· Web 服务命令行管理工具,用于设计、开发及管理 Web 服务。

(3) 私有 UDDI 服务器,UDDI 用户可以控制访问基本 UDDI 数据结构商业实体、商

第 10 章 使用 W eb 服务

— 277 —

业服务、绑定模板及 tModel。

这些技术和工具统称为 Web Services 工具包(WST)。

2. WST 简介

Web Services 工具包(WST)包含了在 EAServer 服务器中创建和管理 Web 服务的工具

集合,其中包括对标准 Web 服务协议的支持(如,SOAP,WSDL 和 UDDI)及 WSDL 文档生

成、客户端生成、UDDI 注册和 SOAP 管理工具。

开发人员可以借助 Eclipse 图形化开发平台部署、测试 Web 服务。Eclipse 是开放源代

码软件开发平台,提供了强大的开发环境,开发商可以通过插件形式将不同的功能嵌入到

Eclipse 中,以支持广泛的应用。Sybase 公司开发的 Sybase Web Service 就是作为提供 Web

服务开发的一种插件支持,和 EAServer 联合使用可以满足 Web 服务开发的各种需求,包

括创建 Web 服务描述文档 WSDL,创建第 3 方 Web 服务的测试客户端,发布 Web 服务

以满足企业内部网络的 Web 服务需求及对 Web 服务的各种管理等。

10.2.2 EAServer 安装与配置管理

本节所创建的实例要求应用服务器为 EAServer 5.0 Beta 版,可以从 Sybase 网站上

http://crm.sybase.com/sybase/www/IPG/EAServerBeta50_072803.jsp下载。

1. 安装 EAServer 应用服务器

将下载的 sEAServer500-43P3-10win.zip 解压缩到临时目录下,然后单击 Setup.exe 启

动安装程序。EAServer5.0 安装方便,只需按照默认值单击确定即可,安装步骤如下。

(1) 选择安装软件所属地区,默认的安装地区没有 China,所以需要从下拉列表框中

选择 HongKong 选项,如图 10-2 所示。

图 10-2 从下拉列表框中选择 HongKong 选项

(2) 选择安装类型,需要选中 Custom(用户 )安装,然后选择安装的组件,确保

Pow erBuilder9.0 基础应用与系统开发

— 278 —

JDK1.4、PowerBuilderV 9.0.0、WST Runtime(incudes WST Client Runtime)、Eclipse Based

Development Tool 选项选中,其他组件使用默认选项即可,如图 10-3 所示。

图 10-3 选择安装组件

(3) 输入安装 JDK 的路径,如果本机没有安装 JDK,直接单击 Next,安装程序会默

认安装 JDK1.3 和 JDK1.4,如图 10-4 所示。

图 10-4 安装 JDK

(4) 安装程序默认安装新的 Eclipse 开发工具,如果本机已安装了 Eclipse 2.1 或 2.1.1

开发工具,用户需要选中 Use existing Eclipse,然后输入安装路径,如图 10-5 所示。

图 10-5 安装 Eclipse 开发工具

(5) 如果没有特别声明,用户只需按照默认设置,单击 Next 即可,然后安装程序开

第 10 章 使用 W eb 服务

— 279 —

始安装组件,如图 10-6 所示。

图 10-6 开始安装组件

EAServer 服务器安装成功后,默认安装在路径 C:\Program Files\Sybase\EAServer 下,

Eclipse 默认安装在 C:\Program Files\Sybase\Shared\Eclipse 下。

2. 配置管理 EAServer 应用服务器

EAServer 服务器安装成功后,需要通过 Sybase Central(控制台)进行配置和管理。

(1) 启动 EAServer 服务器

默认的 EAServer 服务器名为 Jaguar,可以从开始菜单程序管理启动服务器,选择开

始/程序/Sybase/EAServer5.0.0 菜单,单击 Jaguar Server;或进入 EAServer 安装路径下的

bin 子目录,双击 ServerStart.bat 启动服务器。当出现 Done binding to all configured Name

Servers 字符串,表示 EAServer 服务器启动成功,可以开始各种服务。EAServer 启动时系

统自动设置所需的环境变量,如图 10-7 所示。

图 10-7 启动 EAServer 服务器

(2) 简单配置管理 EAServer 服务器

从开始菜单程序管理启动控制台,选择“开始”|“程序”|Sybase/EAServer5.0.0 菜

单,单击 Jaguar Manager,或进入 EAServer 安装路径下的 bin 子目录,双击 jagmgr.bat 启

动控制台。进入控制台界面,在菜单栏中选择工具/连接,在弹出的登录页面中输入控制

台管理员账户、密码、主机名、端口号等连接信息。控制台默认的连接信息为:管理员账

号为 jagadmin,密码为空,主机名为 localhost,端口号为 9000。连接时需要修改 localhost

为主机名,单击确定后自动连接到 Jaguar Server,如图 10-8 所示。

登录成功,管理员可以从菜单栏中选择工具/连接配置文件,手动创建新配置文件,

保存连接信息,并设置为默认连接配置文件。以后每次启动控制台,系统会使用默认连接

配置文件自动连接 EAServer 服务器,如图 10-9 所示。

Pow erBuilder9.0 基础应用与系统开发

— 280 —

图 10-8 控制台登录界面 图 10-9 创建连接配置文件

为了确保 EAServer 的安全性,可以更改默认管理员 jagadmin 的密码。在控制台左侧

的列表视图中选中服务器/Jaguar Server 选项并右击,在弹出菜单中选择“属性”,在弹出

的属性页面中选择“安全”标签页,单击“设置 jagadmin 口令”,然后输入新密码,单击

“确定”按钮保存设置,系统提示密码更改成功。如图 10-10 所示。

图 10-10 更改 jagadmin 口令

通过控制台,管理员可以实现 EAServer 服务器的各种配置管理,其中包括登录人员

的角色、监听器、消息服务、日志、服务器运行监控、Web 应用程序发布等,如图 10-11

所示。这里采用系统默认配置即可满足本节 Web 服务创建实例要求。

第 10 章 使用 W eb 服务

— 281 —

图 10-11 控制台主界面

10.2.3 Web 服务创建实例

本节主要介绍如何使用 PowerBuilder 和 EAServer 应用服务器创建一个简单的 Web 服

务实例,其主要步骤如下。

1. 创建并部署 EAServer 组件

EAServer 组件实际上是 PowerBuilder 自定义用户类非可视化对象(NVO),该组件封

装 了其具 有的属 性和 方法 ,用户 可以通 过 EAServer 服 务器 实现用 户商 业逻辑 。

PowerBuilder 提供了 EAServer 组件向导,这样可以方便、快捷地引导用户创建新的

EAServer 组件,并通过简单步骤实现组件的部署。具体步骤如下。

(1) 启动 EAServer 服务器(Jaguar Server)。

(2) 创建 EAServer 配置文件(Profile)。

从菜单栏中选择 Tools| EAServer Profile..选项,或直接在工具栏中单击 EAServer

Profile 按钮,在弹出的 EAServer Profiles 对话框中单击 Add 按钮,在 Edit EAServer

Profile 对话框中输入配置文件名、EAServer 服务器名、端口号、登录名和密码,这是连

接 EAServer 服务器的必要信息。EAServer 服务器名称可以是主机名,也可以是主机 IP

地址,默认端口号为 9000,默认登录管理员为 jagadmin,密码为空(管理员可以在

EAServer 控制台创建新用户或更改默认登录账号密码)。然后单击 Test 按钮,出现成功提

示窗口,则说明当前配置文件正确,单击 OK 按钮保存配置,最后关闭 EAServer Profiles

Pow erBuilder9.0 基础应用与系统开发

— 282 —

对话框,如图 10-12 所示。

图 10-12 设置 EAServer 配置文件

(3) 创建 Workspace(工作区)

从菜单栏中选择 File|New 选项或直接在工具栏中单击 New 按钮,在弹出的对话框中

选择 Workspace 标签页,选中 Workspace 图标,新建一个名称为 ServerComponent.pbw 的

工作区,如图 10-13 所示。

图 10-13 新建工作区

(4) 使用向导创建 EAServer 组件

从菜单栏中选择 File|New 选项或直接在工具栏中单击 New 按钮,在弹出的对话框中

选择 Target 标签页,选中 EAServer Component 并单击 OK 按钮,如图 10-14 所示。

第 10 章 使用 W eb 服务

— 283 —

图 10-14 使用 EAServer Component 向导创建 EAServer 组件

在向导的引导下用户需要输入创建 EAServer 组件的必要信息,其中包括应用程序名

称 、库文 件搜索路 径, PowerBuilder 应用 对象名 称、 EAServer 组件名称 及部署 到

EAServer 服务器上的包名,如图 10-15 所示。

图 10-15 根据向导提示输入组件信息

Pow erBuilder9.0 基础应用与系统开发

— 284 —

选择 EAServer 配置文件,配置 EAServer 服务器的连接信息,如图 10-16 所示。

图 10-16 选择 EAServer 配置文件

其余按照向导默认设置一步步创建组件,如图 10-17 所示。

图 10-17 选择向导默认设置

第 10 章 使用 W eb 服务

— 285 —

完成以上设置后,向导自动生成 To-DoList(任务列表),显示组件的所有创建信息,

单击 Finish 按钮完成向导,如图 10-18 所示。

图 10-18 向导生成的 To-DoList 列表

(5) 为用户对象添加 HelloWorld 函数

使用向导创建组件成功后,在当前工作区列表中会增加新的项目和用户对象图标,双

击用户对象,在右侧 User Object Painter(用户对象画板)中为新建的用户对象增加一个函数

名为 HelloWorld 的函数,此处设置函数返回类型为 string,参数名为 name,参数类型为

string,并在代码编辑区中输入以下代码:

return “PowerBuilder 欢迎你,”+name+“!!!”

编辑完成之后对以上所作工作进行保存,如图 10-19 所示。

图 10-19 为用户对象增加 HelloWorld 函数

(6) 部署 EAServer 组件

组件创建成功后需要将其部署到 EAServer 服务器上。EAServer 服务器是通过

Pow erBuilder9.0 基础应用与系统开发

— 286 —

PowerBuilder 虚拟机 (PBVM)实例化 EAServer 组件的,所以要实例化组件必须确保

EAServer 服务器上安装的虚拟机和 PowerBuilder 开发环境中的虚拟机版本一致。

EAServer5.0 Beta 服务器允许不同版本的 PBVM 存在于同一台服务器上,以部署来自不同

的 PowerBuilder 客户端开发的 EAServer 组件。本小节开发的 Web 服务所需要的虚拟机最

低支持版本为 PBVM9.0.0。

部署 EAServer 组件时需要关闭当前所有画板,在工作区列表中选择向导创建的项目

并右击,在弹出菜单中选中 Deploy,PowerBuilder 自动将用户创建的 EAServer 组件部署

到 EAServer 服务器上,同时将部署信息显示在输出列表框中,如图 10-20 所示。

图 10-20 部署 EAServer 组件

组件部署成功后,会在 Jaguar Manager(控制台)列表视图中的服务器/Jaguar/已安装的

软件包中找到刚部署的软件包 EASer_comp 及 EAServer 组件 n_server_comp。用户可以设

置组件属性,这里采用默认属性设置。如图 10-21 所示。

图 10-21 部署到服务器上的 EAServer 组件

第 10 章 使用 W eb 服务

— 287 —

2. 将组件配置为 Web 服务

EAServer 服务器提供 Web Services Toolkit 开发工具(WST)将成功部署的 EAServer 组

件配置(expose)为 Web 服务。

(1) 启动 Eclipse,运行 Sybase Web Services 插件。

进 入 Eclipse 安 装 路 径 下 , 双 击 starteclipse.bat, 启 动 Eclipse 。 从 菜 单 栏 选 择

Window|Open Perspective,单击 Sybase Web Services,打开 Sybase Web Services 视图,如

图 10-22 所示。Sybase Web Services 插件是由 Sybase 公司自主开发并提供对 Web 服务全

面支持的插件。通过和 EAServer 集成应用,可以实现将部署到 EAServer 的组件配置为

Web 服务,发布 Web 服务到 Sybase 私有 UDDI 注册中心服务器(UDDI Server),创建第 3

方 Web 服务测试的 Java 客户端,同时提供监控 Web 服务 SOAP 消息交换的工具 Soap

Inspector。

图 10-22 运行 Sybase Web Services 插件

(2) 连接 EAServer 服务器。

在 Sybase Web Services 视图列表中选择 Default on localhost:8080 并右击,在弹出菜单

中先选择 Profile Properties,配置 EAServer 服务器的连接信息,然后选择 Connect,连接

到 EAServer 服务器,如图 10-23 所示。

图 10-23 连接 EAServer 服务器

Pow erBuilder9.0 基础应用与系统开发

— 288 —

(3) 将组件配置为 Web 服务。

WST 会自动检索 EAServer 服务器上部署成功的组件,然后显示在 Sybase Web

Services 视图列表中 Default on localhost:8080/Other Components 目录下。扩展 Other

Components 节点,然后选中刚才发布成功的 n_server_comp 组件并右击,在弹出菜单中选

择 Quickly Expose as Web Service,WST 开发工具会按照默认值将组件配置为 Web 服务:

设置组件名为默认的 Web 服务名,封装 Web 服务的包名默认为 ws,然后将 ws 软件包部

署到 EAServer 服务器上,如图 10-24 所示。开发人员也可以选择 Expose as Web Service,

手动输入 Web 服务名称和封装的包名等信息。

图 10-24 使用 Quickly Expose as Web Service 配置 Web 服务

(4) 验证 Web 服务。

Web 服务配置成功后,刷新 Sybase Web Services 视图,然后展开 Default on

localhost:8080/Web Services/ws 目录,就会找到新发布的 Web 服务。也可以登录到

EAServer 服务器控制台,在控制台列表视图中的服务器/已安装的 Web 应用程序文件夹中

找到 ws 包,如图 10-25 所示。

图 10-25 验证 Web 服务

第 10 章 使用 W eb 服务

— 289 —

(5) 浏览 WSDL 文档。

Web 服务发布成功,WST 自动为部署的 Web 服务创建 WSDL 文档,用户可以通过

浏览器浏览 WSDL 文档查看该 Web 服务的描述。WSDL 发布的默认路径为:http://主机

名 :8080/ 包 名 /services/Web 服 务 名 ?wsdl , 本 实 例 的 WSDL 文 档 路 径 为 :

http://lwj:8080/ws/services/n_server_comp?wsdl,如图 10-26 所示。除此之外,还可以在控

制台中选中 Web 服务软件包并右击,在弹出菜单中选择属性,选中 Servlet/JSP Mapping

标签页,修改 WSDL 的 URL 映射路径,默认路径为/services/*。

图 10-26 本实例的 WSDL 文档

到目前为止,创建 Web 服务实例已经完成。使用 PowerBuilder 和 EAServer 创建的

Web 服务,支持 JSP、.NET 和 Java 第 3 方客户端语言调用,下节实例中我们给出

PowerBuilder 9.0.1 客户端调用本节 Web 服务的实例。用户也可以直接在 Eclipse 工具中生

成 Web 服务 Java 客户测试脚本,并将其集成到 Java 应用中。PowerBuilder 9.0 的 Web 服

务新功能也支持 JSP 客户端调用,这里就不再一一描述。

需要说明的是,本节实例仅是作为初学者接触学习了解 PowerBuilder 和 EAServer 创

建 Web 服 务 的 简 单 实 例 , 如 需 深 入 学 习 相 关 知 识 , 请 到 Sybase 网 站

http://www.sybase.com的技术开发支持(SDN)中心查阅。

Pow erBuilder9.0 基础应用与系统开发

— 290 —

10.3 Web Services 应用实例

PowerBuilder 9.0 企业版增加了对 Web 服务的支持,其应用程序可以作为 Web 应用的

客 户 端 。 通 过 使 用 SOAP 和 WSDL 协 议 , 远 程 发 布 的 Web 服 务 就可 以 和 本 地

PowerBuilder 应用集成在一起。用户可以使用 Web 服务代理向导来完成 Web 服务的应用

集成,屏蔽了对 SOAP 规范和模式、XML Schema 规范或 WSDL 规范和模式的扩展知识

的掌握,简化了 Web 服务应用过程,加强了分布式应用之间的通信。

本实例介绍如何使用 PowerBuilder 9.0.1 企业版的 Web 服务代理向导应用上一节创建

的 Web 服务。

10.3.1 安装 PowerBuilder 9.0.1 企业版升级包

在此使用 EAServer5.0 作为 Web 服务器,使用 PowerBuilder 9.0.1(6533 版本)及以上版

本作为客户端环境。可以从 PowerBuilder 9.0.1 企业版升级包的发布光盘上运行安装程

序,升级程序默认安装在 PowerBuilder 9.0 企业版的安装路径下,安装时需注意以下几

项。

(1) 与 PowerBuilder 9.0.1 企业版兼容的操作系统平台有:Windows NT(SP6)、

Windows 2000(SP2)或 Windows XP,同时还对 Windows 2003 及 Windows 98 提供有限支

持 。 详 情 请 参 阅 PowerBuilder 9.0.1 的 发 布 公 告 (Release Bulletin) , 网 址 为 :

http://www.sybase.com/detail/1,6904,1026720,00.html。

(2) 安装前确保已安装了 PowerBuilder 9.0 企业版,同时备份 PowerBuilder 9.0 安装路

径下的/Shared/PowerBuilder 路径中的内容。

(3) 为了能在 EAServer 应用服务器上运行 PowerBuilder 组件,必须确保 EAServer 服

务器上安装的运行时共享库版本和 PowerBuilder 开发环境中的运行时共享库版本一致,

详细内容可以参看 EAServer 的发布公告的产品兼容一节。如果 EAServer 服务器没有安装

PowerBuilder 9.0.1 虚拟机,那么必须运行光盘中的 PBVM 安装程序,更新 PowerBuilder

运行文件。

(4) 重启计算机。

PowerBuilder 9.0.1(6533 版本)企业版修改了 PowerBuilder 9.0(5507 版本)企业版只能编

译 EAServer4.2 版本创建的 Web 服务客户端代理对象的 Bug,Web 服务提供了更全面的客

户端支持。

10.3.2 使用向导创建 Web 服务代理对象

通过 SOAP 消息调用 Web 服务需要序列化和逆序列化数据交换的数据类型,构建和

解析基于 XML 协议的 SOAP 消息。PowerBuilder 的 Web 服务的客户端代理提供了以上功

能的实现,开发人员只需通过代理对象将 Web 服务的调用同客户端应用集成在一起,就

可以实现复杂的分布式计算。

PowerBuilder 企业版提供了 Web 服务代理创建向导,可以帮助开发人员迅速创建

Web 服务代理对象,实现 Web 服务的调用。具体步骤如下。

第 10 章 使用 W eb 服务

— 291 —

(1) 启动 EAServer 服务器

(2) 创建 Workspace(工作区)和应用程序

从菜单栏中选择 File|New 选项或直接在工具栏中单击 New 按钮,在弹出的对话框中

选择 Workspace 标签页,选中 Workspace 图标,新建一个名称为 PB901client.pbw 的工作

区。然后选择 Target 标签页,选中 Application 图标,新建一个名称为 pb901client 的应

用。

(3) 使用向导创建 Web 服务代理对象

从菜单栏中选择 File|New 选项或直接在工具栏中单击 New 按钮,在弹出的对话框中

选择 Target 标签页,选中 Web Service Proxy Wizard 图标,并单击 OK 按钮,启动 Web 服

务代理创建向导,如图 10-27 所示。

图 10-27 使用 Web 服务代理向导创建代理对象

(4) 在 WSDL File Name 文本框中输入已经发布的 Web 服务的 WSDL 文档绝对路

径,本实例 WSDL 路径为http://lwj:8080/ws/services/n_server_comp?wsdl。

图 10-28 输入 WSDL 文档绝对路径

Pow erBuilder9.0 基础应用与系统开发

— 292 —

(5) 选择 WSDL 文档中描述的服务和端口:n_server_comp,n_server_comp,每个代

理对象只能选择一个服务和一个端口,如图 10-29 所示。

图 10-29 选择 WSDL 描述的服务和端口

(6) 在 Prefix For Proxy Name 文本框中输入代理对象名称的前缀,默认为空值,向导

根据输入值自动生成代理对象名称,如图 10-30 所示。

图 10-30 输入创建代理对象名称前缀

(7) 单击 Finish 按钮,Web 服务代理向导完成所有操作,如图 10-31 所示。

第 10 章 使用 W eb 服务

— 293 —

图 10-31 向导完成代理对象创建

(8) 双 击 左 侧 系 统 列 表 视 图 中 代 理 向 导 创 建 的 项 目 , 然 后 从 菜 单 栏 中 选 择

Design|Depoly Project , PowerBuilder 自 动 部 署 项 目 , 同 时 生 成 代 理 对 象

proxyn_server_comp 和 2 个异常处理对象 tns1_userexception、tns2_pbuserexception,如图

10-32 所示。

图 10-32 系统生成的代理对象和异常处理对象

10.3.3 调用 Web 服务

1. 添加 PBSoapClient90.pbd 文件搜索路径

当使用代理对象创建 Web 服务客户端应用程序时,需要引用 PBSoapClient90.dll 文件

中 定 义 的 SOAP 对 象 集 合 。 为 了 在 PowerBuilder 中 使 用 该 动 态 链 接 库 , 需 要 将

Pow erBuilder9.0 基础应用与系统开发

— 294 —

PBSoapClient90.pbd 动态库文件部署到客户端应用程序搜索路径下。PowerBuilder 的动态

库(PBD)文件使 PowerBuilder 客户端能够方便地使用 DLL 文件中定义好的对象,对用户

来说,就像是在使用 PowerBuilder 自定义用户对象一样。方法是将 PBSoapClient90.pbd 文

件的绝对路径添加到当前应用程序搜索路径下,在左侧系统列表视图中选择 Target 对象

并右击,在弹出菜单中选择属性,将位于 PowerBuilder 安装目录 Shared/PowerBuilder 路

径下的 PBSoapClient90.pbd 文件的绝对路径添加到库路径搜索页面中,如图 10-33 所示。

图 10-33 添加 PBSoapClient90.pbd 文件搜索路径

2. 创建 Web 服务客户端程序

有了前面的准备工作,就可以创建 Web 服务客户端编码实例。其步骤如下。

(1) 在客户端应用程序中添加一个名为 w_ws 的新窗体,然后在该窗体中放置一个用

于显示信息的静态文本、一个用于显示 Web 服务调用结果的单行文本编辑框及一个用于

触发 Web 服务调用的命令按钮,各控件属性说明见表 10-1。

表 10-1 w_ws 窗体控件属性

控 件 属 性 属性取值 st_1 Text 调用 EAServer 服务器上的 Web 服务 sle_1 Text 默认为空值 cb_1 Text 调用

(2) 使用代理对象调用 Web 服务之前首先需要创建一个 SOAP 连接,并通过

SoapConnection 对象实例化连接对象,使客户端可以连接到欲访问的 Web 服务的 SOAP

服务器上。然后使用 CreateInstance 方法创建 Web 服务访问的客户端代理实例,开发人员

通过该实例可以访问 Web 服务定义的方法及属性。

PowerBuilder 还提供了异常处理机制,当连接 SOAP 服务器出错或调用 Web 服务方

法出错时,这些错误会自动转换成 SoapException 异常对象,并抛回到正在执行调用的脚

第 10 章 使用 W eb 服务

— 295 —

本中,由脚本异常处理程序进行处理,从而防止了因 Web 服务调用出错导致应用程序的

异常终止。

在 w_ws 窗体的 cb_1 按钮的 clicked 事件中编写代码如下:

SoapConnection soap_conn

proxyn_server_comp proxy

long rVal

//创建 Soap 连接

soap_conn = create SoapConnection

//创建代理

rVal = soap_conn.CreateInstance(proxy, "proxyn_server_comp")

//判断代理创建是否成功

if rVal = 0 then

try

//通过代理对象调用 Web 服务定义的 helloworld 方法

sle_1.text = proxy.helloworld("Mr Stone")

catch( SoapException e )

//Web 服务调用异常处理,并显示出错信息

sle_1.text = "Web 服务调用出错!!"+e.text

end try

//代理创建失败

else

sle_1.text="创建代理失败!!"

end if

//释放 Soap 连接对象

destroy soap_conn

(3) 在应用程序的 Open 事件中编写代码,用于在应用程序开始运行时显示 w_ws 窗

体,具体代码如下:

Open(w_ws)

(4) 保存、编译并运行程序,然后单击“调用”按钮开始调用 Web 服务,在窗体的文

本框中显示调用结果,如图 10-34 所示。

图 10-34 Web 服务调用实例返回结果

Pow erBuilder9.0 基础应用与系统开发

— 296 —

10.4 练习题

创建一个 Web 服务,提供加、减、乘、除基本运算,然后分别使用 PowerBuilder 客

户端和.NET 客户端调用该 Web 服务。

解答提示

使用.NET 作为 Web 服务应用客户端环境,其创建过程和 PowerBuilder 创建步骤有点

类似,均需要先建代理对象,然后通过代理对象访问 Web 服务定义的方法函数,但省略

了创建 SOAP 连接。调用 Web 服务代码如下:

[C#]

private void Button1_Click(object sender, System.EventArgs e)

{

lwj2.n_exercise proxy=new lwj2.n_exercise();

try

{

TextBox3.Text=System.Convert.ToString(proxy.add(System.Convert.ToDouble(

TextBox1.Text),System.Convert.ToDouble(TextBox2.Text)));

}

catch (Exception ex)

{

Label3.Text="调用出错!!!信息为:"+ex.Message;

}

}

调用结果如图 10-35 所示。

图 10-35 .NET 环境调用 exercise Web 服务中的 add 方法

第 11 章 PB N I及第 3 方应用服务

本章介绍关于 PowerBuilder 9.0 的新技术:PowerBuilder Native Interface(PowerBuilder

本地接口—PBNI),主要介绍 PBNI 的基本原理及其构成元素,并在此基础上阐述

PowerBuilder 与 C++、JAVA 以及第 3 方组件之间的接口和消息传递原理及方法,最后介

绍两个具体的 PBNI 应用实例。

11.1 PBNI 及第 3 方应用简介

本节首先讨论 PBNI 的主要用途及其简单工作原理,还要介绍 PBNI 的一些基本元

素,并对其软件开发包(SDK)做概要说明,最后探讨 PBNI 和 JNI 的异同。

11.1.1 关于 PBNI

PowerBuilder 9.0 通过 PBNI 向开发人员提供了一个新的编程界面,令用户使用 C 和

C++应用程序工作更轻松。用户可以通过 PowerBuilder 看到第 3 方程序,例如 C++组件,

并且将其转换为 PowerBuilder 对象。PBNI 也允许开发人员把 PowerBuilder Virtual

Machine 嵌入外部 C++应用中,这被称为 PowerScript 功能。因此,开发人员可以通过现

有的两种不同且互补的方法实现 PowerBuilder Virtual Machine 与 C++程序的通信。

用 户 可 以 通 过 PBNI 访 问 不 可 视 对 象 , 并 对 其 进 行 可 视 化 控 制 。 PBNI 在

PowerBuilder 中很重要,因为 PowerBuilder 开发人员可通过它操作用 C 或 C++编写的第 3

方应用程序。尽管 PowerBuilder 的许多版本都有该功能,但是在过去还需要掌握有关

C++的详细知识。现在,开发人员只需有限地了解 C++就能轻松地使用该工具。通过

PowerBuilder 9.0 中的库文件和头文件,可以将 C++程序编译成 DLL。编译成 DLL 后,

PowerBuilder 就可以识别里面的方法。生成一个可用于 PowerBuilder 应用的运行时.PBD

库文件,这样 C++程序就成为一个独立的可视的 PowerBuilder 对象。

尽管 PBNI 最开始只是存取和使用 C++的工具,但是其发展已具备更广泛的影响。

PBNI 能充当通往 C++和 Java 的渠道,它也能被用来执行原先不能在 PowerBuilder 内完成

的很多任务。该工具为 PowerBuilder Partner 填补了其可能存在的功能空白,从而为

PowerBuilder 用户提供额外的功能。

PBNI 也是常用的访问 Java 的工具。PowerBuilder 能通过多种方式被 Java 调用,其中

包含扩展集的创建和 Java 代理类的创建。具体来说,Java 类可以通过 Java Native

Interface(JNI)调用 Java 方法,而 Java 方法通过 PBNI 调用 PowerBuilder。

通过 PBNI 所激活的 PowerBuilder 扩展功能,举例如下。

(1) 封装 COM 组件,该组件因为引用自定义的 COM 界面而不能映射到任何

PowerBuilder 数据类型。

(2) 作为 PowerBuilder 的接口,通过使用 Adaptive Server Anywhere 的 Dbtools(需要回

Pow erBuilder9.0 基础应用与系统开发

— 298 —

调函数功能)来备份和管理数据库。

(3) 封装 GNU GhostScript 程序,允许将少量 DataWindow 对象存储成 PDF。

(4) 装所有提供标准应用的开放源代码 C++库。

11.1.2 PBNI 的基本元素

本部分对 PBNI 的基本元素作一简要介绍,PBNI 有 4 类基本元素:接口、结构、全

局函数和帮助器类。

(1) 接口:主要包括 IPB_VM、IPB_Session、IPB_Value 和 IPB_Arguments。

IPB_Value 接口表示一个 PowerBuilder 数值,该接口既可以提供 PowerBuilder 的一个

标准数据类型,如 int、long、string 等,又可以提供每一个变量的详细信息,如是否为

空、访问权限等。IPB_Arguments 接口表示传递给 PowerScript 函数的参数。

所有的 PowerBuilder 本地类要么继承于 IPBX_NonVisualObject 接口要么继承于

IPBX_VisualObject 接口,而这两个接口又继承于 PBX_UserObject 接口。在此需要注意的

是:Invoke 方法必须在子类中实现,这样 PowerBuilder 才能调用本地的方法。有关各个

接口的详细内容将在下面介绍。

(2) 结构:PBNI 有两类数据结构,分别是 PBCallInfo 和 PBArrayInfo。

(3) 全局函数:每一个 PowerBuilder 扩展对象必须导出全局函数,PBVM 通过此函数

创建一个对象的实例并且使用该实例的方法。

(4) 帮助器类:帮助器类使用户使用 PBNI 进行编程时更容易。最常见的帮助器类有

PBObjectCreator,PBArrayAccessor 和 PBEventTrigger。

对于 PBNI 的基本元素除按其类型进行分类外,还可以按其不同的功能进行分类。如

果按照不同的功能进行分类,通常分为 3 类:与会话及对象控制有关的元素、与函数调用

有关的元素以及与扩展功能有关的元素,下面将对其功能进行描述。

11.1.2.1 与会话及对象控制有关的元素

这种元素主要为扩展功能调用提供一条通道,其本身并不实现有关内容性扩展。最常

见 的 与 会 话 及 对 象 控 制 有 关 的 元 素 有 IPB_VM 、 IPB_Session 、 IPB_Value 及

IPB_Arguments 等。

1. IPB_VM

该接口使用在第 3 方应用程序中,用来载入 PowerBuilder 应用,与 PB 虚拟机进行交

互。该接口有两个方法:CreateSession 和 RunAplication。

(1) CreateSession

语法

CreateSession(LPCTSTR applicationName , LPCTSTR* libraryList , pbuint

numLibs,IPB_Session** session)

applicationName:当前的应用对象名(小写)。

libraryList:包含有所要调用的对象和函数的 PowerBuilder 应用库列表。

第 11章 PBN I及第 3 方应用服务

— 299 —

numLibs:库列表中库的个数。

session:指向 IPB_Session*的指针。

例 11.1:本例简单地介绍如何使用库 mydemo.pbl 创建 IPB_Session 方法。

IPB_Session* session;

IPB_VM* vm = NULL;

fstream out;

ifstream in;

PBXRESULT ret;

HINSTANCE hinst=LoadLibrary("pbvm90.dll");

if ( hinst== NULL) return 0;

out<< "Loaded PowerBuilder VM successfully!"<<endl;

P_PB_GetVM getvm = (P_PB_GetVM)GetProcAddress(hinst, "PB_GetVM");

if (getvm == NULL) return 0;

getvm(&vm); if (vm == NULL) return 0;

static const char *liblist[] =

{

"mydemo.pbl"

};

ret= vm->CreateSession("mydemo", liblist, 1, &session);

if (ret != PBX_OK)

{

out << "Create session failed." << endl;

return 0;

}

out << "Create session succeeded!" <<endl;

(2) RunAplication

语法

RunApplication(LPCTSTR applicationName, LPCTSTR* libraryList, pbuint

numLibs, LPCSTR commandLine, IPB_Session** session)

applicationName:要运行的应用对象名(小写)。

libraryList:包含有所要调用的对象和函数的 PB 应用库列表。

numLibs:库列表中库的个数。

commandLine:传递给应用对象的参数。

session:指向 IPB_Session*的指针。

例 11.2 下面的这段代码实现首先将 PBVM 装入内存,接着使用 RunApplication 方

法调用名为 runapp 的应用程序。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message,

WPARAM wParam, LPARAM lParam)

{

LPCTSTR szHello = "Hello world"; // Provide command line parameters (employee ids)

// to be passed to the PowerBuilder application

Pow erBuilder9.0 基础应用与系统开发

— 300 —

LPCTSTR szcommandline = "102 110";

int wmId, wmEvent, ret;

PAINTSTRUCT ps;

HDC hdc;

switch (message)

{

case WM_CREATE:

{

hPBVMInst = ::LoadLibrary("pbvm90.dll");

P_PB_GetVM getvm = (P_PB_GetVM)

GetProcAddress(hPBVMInst,"PB_GetVM");

IPB_VM* vm = NULL;

getvm(&vm);

static const char *liblist [] ={"runapp.pbd"};

vm->RunApplication("runapp", liblist, 1,

szcommandline, &session);

break;

}

}

}

IPB_Value 和 IPB_Arguments 这 两 个 接 口 主 要 实 现 在 PowerBuilder 虚 拟 机 和

PowerBuilder 扩展类之间传递数值。

2. IPB_Arguments

本接口有两个方法:GetAt 和 GetCount。

GetAt 方法返回指向接口 IPB_Value 的指针。

语法

GetAt ( pbint index )。

例 11.3 本例中,GetAt 方法获得结构 PBCallInfo 的第 1 个值,在此假设值已经由调

用的函数传递给 PBCallInfo 结构的变量 ci。

PBCallInfo ci;

LPCSTR myPBNIObj = NULL;

IPB_Value* pArg0 = ci->pArgs->GetAt(0);

if (!pArg0->IsNull())

{

pbstring t = pArg0->GetString();

if (t != NULL)

myPBNIObj = session->GetString(t);

}

GetCount 获得一个 PBCallInfo 变量的返回值的个数。

例 11.4 本例使用了一个 FOR 循环,利用 GetCount 函数来确定循环次数以处理不同

的返回类型。

int i;

第 11章 PBN I及第 3 方应用服务

— 301 —

for (i=0; i < ci-> pArgs -> GetCount();i++)

{

pbuint ArgsType;

if( ci -> pArgs -> GetAt(i) -> IsArray())

pArguments[i].array_val =ci -> pArgs -> GetAt(i) -> GetArray();

continue;

}

if( ci -> pArgs -> GetAt(i) -> IsObject())

{

if (ci -> pArgs -> GetAt(i) -> IsNull())

pArguments[i].obj_val=0;

else

pArguments[i].obj_val =ci -> pArgs -> GetAt(i) -> GetObject();

continue;

}

...

3. IPB_Value

这个接口是用来处理不同的数据类型的,通过该接口可以将函数返回值类型转化为当

前应用所需类型。

方法:Get<type>(type 可由具体的数据类型 Int、Long、Bool、Char、Array 等替换),

如 GetClass , GetType , IsArray , IsByRef , IsEnum , IsNull , IsObject , SetToNull ,

Set<type>(type 可由具体的数据类型 Int、Long、Bool、Char、Array 等替换)。

有关各种方法的具体应用较为简单,请读者查询 PowerBuilder 手册。

在例 11.3 和 11.4 中,都用到了一个变量 ci,这是一个结构变量,下文予以介绍。

11.1.2.2 与函数调用有关的元素

1. PBCallInfo

PBCallInfo 是一个 C++的数据结构,它在 PBNI 和 PowerBuilder 之间起到一个保存调

用结果和返回类型的作用,其成员表见表 11-1 所示。

表 11-1 PBCallInfo 结构成员

成 员 类 型 描 述 pArgs IPB_Arguments* 用来访问返回参数的接口 returnValue IPB_Value 调用后保存返回数据 returnClass pbclass 调用后保存返回类

2. PBArrayInfo

同 PBCallInfo 一样,都是用来保存返回数据的 C++数据结构,与 PBCallInfo 不同的

是,PBArrayInfo 保存的对象主要是数组。具体请参看 PowerBuilder 手册。

11.1.2.3 与扩展功能有关的元素

与 扩 展 功 能 有 关 的 元 素 主 要 包 括 IPBX_NonVisualObject 、 IPBX_VisualObject 、

Pow erBuilder9.0 基础应用与系统开发

— 302 —

IPBX_UserObject 和 IPBX_Marshaler,下面将一一给以说明。

1. IPBX_UserObject

该类是所有 PowerBuilder 本地类的父类,封装了两个方法 Invoke 和 Destroy。这两个

方法必须在用户的扩展对象中实现,当用户在 PowerBuilder 中创建了一个扩展对象实

例,并调用其方法时,PBNI 自动调用 Invoke 方法,而且在不需要实例时,自动调用

Destroy 销毁进程实例。

语法

Destroy( )

Invoke(IPB_Session * session, pbobject obj, pbmethodID mid, PBCallInfo

*ci)

例 11.5 本例实现一个扩展对象 MyPBNIClass 的 Invoke 方法和 Destroy 方法。

PBXRESULT MyPBNIClass::Invoke(IPB_Session *session,pbobject

obj,pbmethodID mid,PBCallInfo *ci)

{

PBXRESULT result = PBX_OK;

switch (mid)

{

case mFuncA:

result = FuncA(session, obj, ci);

break;

case mFuncB:

result = FuncB(session, obj, ci);

break;

case mFuncC:

result = FuncC(session, obj, ci);

break;

default:

result = PBX_E_INVOKE_FAILURE;

break;

}

return PBX_OK;

} void MyPBNIClass::Destroy()

{

delete this;

}

2. IPBX_NonVisualObject

IPBX_NonVisualObject 继承于 IPBX_UserObject,是所有非可视扩展对象的父类。该

类从父类继承了 Invoke 方法和 Destroy 方法,见 IPBX_UserObject 的介绍。

3. IPBX_VisualObject

IPBX_VisualObject 继承于 IPBX_UserObject,是所有可视扩展对象的父类。

第 11章 PBN I及第 3 方应用服务

— 303 —

除继承了 IPBX_UserObject 的 Invoke 方法和 Destroy 方法之外,该类还有自己的扩展

方法 CreatControl、GetEventID 和 GetWindowClassName。

CreatControl 创建一个窗口控制并返回其句柄给 PBVM。

语法

CreateControl(DWORD dwExStyle, LPCTSTR lpWindowName, DWORDdwStyle, int x,

int y, int nWidth, int nHeight, HWND hWndParent,HINSTANCE hInstance)

dwExStyle:扩展窗口类型

lpWindowName:窗口名

dwStyle:窗口类型

x, y, nWidth, nHeight, :窗口位置,大小

hWndParent:本窗口的拥有者句柄

hInstance:窗口句柄

例 11.6 本例可从 sybase 网站上得到 CreatControl 的实现代码。

LPCTSTR CVisualExt::GetWindowClassName()

{

return s_className;

}

HWND CVisualExt::CreateControl

(

DWORD dwExStyle, // extended window style

LPCTSTR lpWindowName, // window name

DWORD dwStyle, // window style

int x, // horizontal position of window

int y, // vertical position of window

int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

HINSTANCE hInstance // handle to application

instance

)

{

d_hwnd = CreateWindowEx(dwExStyle, s_className,

lpWindowName, dwStyle, x, y, nWidth, nHeight,

hWndParent, NULL, hInstance, NULL);

::SetWindowLong(d_hwnd, GWL_USERDATA, (LONG)this);

return d_hwnd;

}

方法 GetEventID 获得一个窗口事件的 ID,GetWindowClassName 返回窗口的名字,

这两个方法使用较为简单,用法和示例读者可参考 PowerBuilder 用册和 sybase 网站。

4. IPBX_Marshaler

本接口用来调用远程方法,以及转换 PowerBuilder 数据格式为用户的通信协议。

Marshaler 扩展是 PowerBuilder 与其他组件之间联系的桥梁,如 EJB、CORBA 对象、Java

Pow erBuilder9.0 基础应用与系统开发

— 304 —

类、Web Service 以及所有其他可以被 C++访问的组件。该接口有 3 个方法 Destroy、

GetModuleHandle 和 InvokeRemoteMethod,Destroy 方法的用法同 IPB_UserObject。这些

方法都必须在用户定义的 Marshaler 扩展对象中实现。

GetModuleHandle 返回包括本地类的 DLL 的句柄。

语法

GetModuleHandle()

例 11.7 下面的代码实现了一个返回 DLL 句柄的 Marshaler 扩展类。

extern pbulong thisModuleHandle;

pbulong SampleMarshaler::GetModuleHandle()

{

return thisModuleHandle;

}

句柄在主模块中。

pbulong thisModuleHandle = 0;

BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID

lpReserved)

{

thisModuleHandle = (pbulong)hModule;

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

InvokeRemoteMethod 方法相当于 IPBX_UserObject 接口的 Invoke 方法,限于篇幅,

不对其作详细阐述,读者可查阅 PowerBuilder 手册。

语法

InvokeRemoteMethod(IPB_Session *session, pbproxyobject obj,

LPCTSTRmethodDesc, PBCallInfo *ci)

11.1.2.4 扩展集必须导出的全局函数

另外还有一类比较重要的元素就是用户须在自己的扩展模块中声明导出的方法。表

11-2 给出了具体方法及其适用范围。

第 11章 PBN I及第 3 方应用服务

— 305 —

表 11-2 PBNI 中的全局函数

方 法 适用范围 PBX_CreateNonVisualObject 扩展包括不可视本地类时 PBX_CreateVisualObject 扩展包括可视本地类时 PBX_GetDescription 所有的 PB 扩展 PBX_InvokeGlobalFunction 扩展包括全局函数时 PBX_Notify 需要初始化一个 Session 时

限于篇幅,本节仅对前 3 个方法做出解释并给出其应用实例。

1. PBX_CreateNonVisualObject

该函数创建一个非可视 PowerBuilder 扩展对象的实例。

语法

PBX_CreateVisualObject(IPB_Session* pbsession, pbobject pbobj,LPCTSTR

xtraName, IPBX_NonVisualObject **obj)

pbsession:IPB Session

pbobj:对应于 PowerBuilder 扩展对象的 PowerBuilder 对象名

xtraName:PowerBuilder 本地类的类名(小写)

obj:待创建的 PowerBuilder 扩展对象

返回类型:实例成功创建,则返回 PBXRESULT. PBX_OK。

例 11.8 本例中,扩展模块包含多个本地类,PBX_CreateNonVisualObject 方法依靠

传递来的类名(字符串)来创建实例。

PBXEXPORT PBXRESULT PBXCALL PBX_CreateNonVisualObject(IPB_Session*

pbsession,pbobject pbobj,LPCTSTR xtraName,IPBX_NonVisualObject **obj)

{

PBXRESULT result = PBX_OK;

string cn(className);

if (cn.compare("class_a") == 0)

{

*obj = new class_a(pbobj);

}

else if (cn.compare("class_b") == 0)

{

*obj = new class_b(pbobj);

}

else if (cn.compare("class_c") == 0)

{

*obj = new class_b(pbobj);

else

{

*obj = NULL;

result = PBX_E_NO_SUCH_CLASS;

} return PBX_OK;

Pow erBuilder9.0 基础应用与系统开发

— 306 —

};

2. PBX_CreateVisualObject

该函数创建一个非可视 PB 扩展对象的实例。

语法

PBX_CreateVisualObject(IPB_Session* pbsession, pbobject pbobj,LPCTSTR

xtraName, IPBX_NonVisualObject **obj)

有关参数的解释同 PBX_CreateNonVisualObject。

例 11.9 本例中,扩展模块包含多个本地类,PBX_CreateNonVisualObject 方法依靠

传递来的类名(字符串)来创建实例。

PBXEXPORT PBXRESULT PBXCALL PBX_CreateVisualObject(IPB_Session*

pbsession,pbobject pbobj,LPCTSTR className,IPBX_VisualObject **obj)

{

PBXRESULT result = PBX_OK;

string cn(className);

if (cn.compare("visualext") == 0)

{

*obj = new CVisualExt(pbsession, pbobj); }

else

{

*obj = NULL;

result = PBX_FAIL;

}

return PBX_OK;

};

3. PBX_GetDescription

PBX_GetDescription 方法是所有的扩展都要导出的方法,该方法将扩展的本地类特征

描述传递给 PBVM。

语法

PBX_GetDescription()

例 11.10 本例的 PBX_GetDescription 方法返回了一个包含有 3 个本地扩展模块的描述。

PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()

{

static const TCHAR desc[] = {

"class class_a from nonvisualobject\n"

"function long meth1(string classpath)\n"

"function string meth2()\n"

"end class\n"

"class class_b from nonvisualobject\n"

第 11章 PBN I及第 3 方应用服务

— 307 —

"subroutine sbrt1()\n"

"subroutine sbrt2()\n"

"function long func1()\n"

"end class\n"

"class class_c from nonvisualobject\n"

"end class\n"

};

return desc;

}

11.1.3 PBNI 的软件开发包(SDK)

PBNI 的 SDK 随着 PowerBuilder 9.0 一起安装,存放在 PowerBuilder 9.0/SDK/PBNI 路

径下,SDK 的两个工具 pbsig 90 和 pbx2pbd 90 则存放在 Shared/PowerBuilder 路径下,这

样系统就可以通过 PATH 变量找到该软件开发包。

PBNI SDK 的组件见表 11-3。

表 11-3 PBNI SDK 组件

组 件 描 述 pbx2pbd90.exe PBNI 的工具,从 C++动态链接库(DLL)产生这 PBD 文件

Pbsig90.exe PBNI 的工具,能够产生 PBL 形式的字符集,其中字符集表示每个函数的返回类型和参数

include/pbni.h 头文件,定义了构建 PB 扩展集所用的结构和接口

include/pbarray.h 头文件,包括 PBNI 元素中的帮助器类

include/pbfield.h 头文件,功能同 pbarray.h 相似

include/pbtraits.h 为 pbarray.h 和 pbfield.h 两个文件提供支持

include/pbext.h 该文件定义了 PBNI 扩展集必须导出的函数

include/pbevtid.h 头文件,将 PB 事件 ID 映射为可视扩展对象的事件名

include/pbnimd.h 该头文件定义了与硬件无关的数据类型

lib/pbni.lib 必须链接到 PB 扩展动态链接库的库文件

wizards/pbext.awx Microsoft VC++向导

pbni90.hlp,pbni90.cnt PBNI 的帮助文件

11.1.4 比较 PBNI 和 JNI

用户如果使用过 JNI(Java Native Interface),那么理解 PBNI 和 JNI 之间的异同就很方

便了。

两种接口的相近之处在于:PBNI 中的 IPB_VM 类似于 JNI 的 JavaVM 类型,

IPB_Session 接口则近似于 JNI 的 JNIEnv。JNI 使用 javap 获得本地类中每个方法的特征编

码字符,而在 PBNI 中,pbsig 9.0 则完成相同的功能。

两种接口的不同之处主要体现在对本地类和本地函数的声明上。

JNI 使用 Native 关键字声明一个函数为本地函数。但是,在对类进行声明时不能简单

地使用 Native 来表示,而必须使用 java 提供的 javah 工具产生一个 C 语言的头文件,该

头文件为每一个本地方法定义一个 C 原型。在实现各自的 C 或 C++函数时,使用#include

将所产生的头文件包含进来。

PBNI 则 提 供 了 一 个 面 向 对 象 的 方 法 , 即 在 C++ 代 码 中 , 通 过 继 承

IPBX_NonVisualObject 或 IPBX_VisualObject,直接声明一个类为本地类。

Pow erBuilder9.0 基础应用与系统开发

— 308 —

11.2 PowerBuilder 与 C++的接口

PowerBuilder 友好的界面及强大的功能使用户很快就能上手应用。有些用户已经有现

成的应用程序,但是由于某些局限性,需要采用其他语言进行功能扩充,这就需要用到

PowerBuilder 与 C++的接口。

11.2.1 创建一个 PowerBuilder 扩展

下面给出了创建一个 PowerBuilder 扩展的具体步骤。

11.2.1.1 决定扩展应具有的特征

PBNI 的运行速度较慢,因此不到迫不得已的时候不要使用 PBNI,所以当要创建一

个 PowerBuilder 扩展时,首先需要确定是否可以采用非可视化扩展的方法。

在决定进行 PowerBuilder 扩展开发之前,用户必须决定这个扩展应具有什么特征,

应该实现哪些功能,甚至需要确定实现这些功能需要什么参数。

11.2.1.2 定义扩展中的类和函数

本 步 骤 阐 述 两 个 必 须 在 C++ 代 码 中 实 现 的 方 法 ( 在 上 一 节 已 经 讨 论 过 的

PBX_GetDescription 和 PBX_CreateVisualObject),PowerBuilder 本地类可以识别这两个方

法并利用其创建实例。

PBX_GetDescription 传递扩展类和全局函数的特征描述给 PowerBuilder,每一个

PowerBuilder 扩 展 都 要 导 出 这 个 方 法 。 在 使 用 pbx2pbd90 工 具 创 建 一 个 可 以 被

PowerBuilder 应用导入的 PBD 文件时,pbx2pbd90 从 DLL 文件中提取 PBNI 扩展的特征

描述。

非可视类继承于 NonVisualObject 系统类及其子类,而一个本地类可继承于一个用

户定义的用户对象,Sybase 推荐使用系统类,每个系统类可提供多个函数、例程和事

件。

有关如何使用 PBX_GetDescription 方法的实例请查看例 11.10。

可视本地类只能继承于 UserObject 系统类,PowerBuilder 虚拟机把所有继承于该类的

对象都看成是可视本地类,所有其他的本地类都被看成是不可视类。

一个扩展可以包括全局函数,下面的一个小例子展示了一个全局函数的特征描述:

"globalfunctions \n" \

"function int g_1(int a, int b)\n" \

"function long g_2(long a, long b)\n" \

"end globalfunctions\n"

在一个扩展中定义全局函数的语法和用法与在一个 PowerBuilder 应用中定义全局函

数的语法和用法相同。

第 11章 PBN I及第 3 方应用服务

— 309 —

注意

PowerBuilder 扩展中的全局函数是不可重载的。

11.2.1.3 声明本地类和全局函数

对每一个不可视扩展支持的本地类,都要声明一个继承于 NonVisualObject 类的

ANSI C++类。

对本地类的声明可以放在一个头文件中,而且必须包括一个 Invoke 和 Destroy 方法。

下面的代码是一个关于本地类声明的例子。

例 11.11 下面的代码声明了一个不可视的本地类 CMyClass,该类中定义了两个方法

funcA 和 funcB。

#include "pbext.h"

class CMyClass : public IPBX_NonVisualObject

{

enum MethodIDs

{

mFunca = 0,

mFuncb = 1

};

public:

// constructor, destructor

CMyClass()

virtual ~CMyClass() // member methods

PBXRESULT Invoke(

IPB_Session *session,

pbobject obj,

pbmethodID mid,

PBCallInfo *ci

);

void Destroy();

private:

void funcA(IPB_Session* session, pbobject obj,

PBCallInfo* ci);

void funcB(IPB_Session* session, pbobject obj,

PBCallInfo* ci);

};

11.2.2.4 实现本地类和全局函数

本地类的实现必须包括 Invoke 和 Destroy 方法以及所有其他声明的方法,其中 Invoke

和 Destroy 是 IPBX_UserObject 接口的方法。

当一个 PowerBuilder 应用调用一个本地方法时,PBVM 调用 Invoke 方法,后者基于

方法名和方法 ID 将调用分发出去,当方法是动态调用时,要使用方法名。

有关 Invoke 和 Destroy 方法的实现上一节已经介绍过了,代码请查看例 11.5。

Pow erBuilder9.0 基础应用与系统开发

— 310 —

11.2.2.5 输出创建类实例的方法

PowerBuilder 采用不同的方法创建非可视实例和可视实例。

对于可视类,只有在一个用到可视类的窗口和可视控制被打开时,系统才会创建一个

可视类的实例。

对于不可视类,在使用 CREATE 语句时,就创建了一个不可视类的实例。

PowerBuilder 通 过 CREATE 语 句 创 建 一 个 实 例 时 , PBVM 调 用 扩 展 中 的

PBX_CreateNonVisualObject 方法,因此,每个包括不可视类的扩展都应导出并实现这个

方法。上一节的例 11.6 含有 PBX_CreateNonVisualObject 方法的实现代码。

11.2.2.6 编译生成 DLL 文件

除了将 PowerBuilder 扩展编译生成 DLL 文件之外,还可将扩展编译成为一个 PBX 文

件,使用时,其功能和 DLL 文件相同。

11.2.2 使用一个 PowerBuilder 扩展

生成了 DLL 文件之后,接着就要进行如下工作,以便在 PowerBuilder 应用中使用

扩展。

11.2.2.1 由 DLL 文件生成 PBD 文件

创建一个 PBD 文件要用到 pbx2pbd90 工具,这个工具随系统安装时已经自动设置了

环境变量,可以在命令行状态下执行。其语法如下:

pbx2pbd90 [+] destination.pbd src1.dll [ src2.dll src3.dll ...srcn.dll ]

在一个 PBD 文件中可以包括多个 DLL 文件,也可以向一个现有的 PBD 文件中加入

新的 DLL 文件,不过这时要在 PBD 文件前使用可选项[+]。

11.2.2.2 将该 PBD 文件加入到目标库列表中

在 PowerBuilder 中要将 PBD 文件加入到 target 列表中,必须确保系统路径可以找到

C++的 DLL 文件。添加了 PBD 文件之后,所有在 DLL 中定义的类都显示在系统树中,

用户可以通过单击其前面的“+”号打开它,查看其属性、方法以及事件,并且可以通过

拖动操作将其添加到用户的 PowerScript 中。

11.2.2.3 使用扩展

在 PowerScript 中,使用非可视扩展中的类和使用普通的用户对象具有相同的操作步

骤:声明一个对象实例,使用 CREATE 语句创建一个实例,调用对象的方法,使用完毕

销毁实例。如果本地类功能不足,还可以通过继承本地类以扩充其功能。

运行时,本地类的实例和普通对象实例的创建方法是相同的。

下面的代码中,扩展模块包括两个不可视本地类:fontcallback 和 fontenumerator。

Nvo_font 是 PowerBuilder 的一个普通用户对象,继承于本地类 fontcallback。本例用

第 11章 PBN I及第 3 方应用服务

— 311 —

CREATE 语句创建了它们的实例:

fontenumerator fe

nvo_font uf

fe = create fontenumerator

uf = create nvo_font

11.2.3 创建和使用可视化扩展

本部分介绍如何创建和使用一个可视化扩展,除了应用目的和部分实现步骤有些差

异,创建一个可视扩展和创建一个不可视扩展基本相似。

11.2.3.1 决定应该实现什么特征

与创建非可视扩展第 1 步进行的工作相同,创建一个可视扩展同样需要确定是否应该

采用可视化扩展的方法,如果采用“所看即所得”的方法能实现的功能就不需要采用可视

化扩展。

在决定进行 PowerBuilder 扩展开发之前,用户必须决定这个扩展应具有什么特征,

应该实现哪些功能,甚至需要确定实现这些功能需要什么参数。

11.2.3.2 定义扩展中的类和函数

对一个可视化对象的定义和对一个非可视化类的定义采用基本相同的步骤,惟一的区

别就是前者由系统类 UserObject 继承而来。

PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()

{

static const TCHAR desc[] = {

"class myvisualext from userobject\n"

"subroutine func_1(int arg1, int arg2)\n"

"subroutine func_2(string arga)\n"

"end class\n"};

return desc;

}

11.2.3.3 声明可视化类和全局函数

声 明 一 个 可 视 化 类 和 声 明 一 个 非 可 视 化 类 的 区 别 在 于 可 视 化 类 继 承 于

IPBX_VisualObject。

11.2.3.4 实现本地类

其实现方法与非可视化类的实现方法相同。

11.2.3.5 Export 方法创建类实例

非可视扩展和可视扩展最大的区别就在于其不同的实例实现方法。下面对可视扩展类

实例的实现进行简要描述。

Pow erBuilder9.0 基础应用与系统开发

— 312 —

当一个驻留有可视本地类的窗口或可视控制被打开时,PBVM 就会自动调用扩展中

的 PBX_CreateVisualObject 方法,用户不必亲自书写 CREATE 语句,而且 PBVM 也会自

动调用 IPBX_VisualObject 的 CreateControl 方法。因此,每个包括可视本地类的扩展都必

须导出 PBX_CreateVisualObject 并且实现 CreateControl 方法。

下面的代码是 PBX_CreateVisualObject 的一个例子:

PBXEXPORT PBXRESULT PBXCALL PBX_CreateVisualObject(IPB_Session*

pbsession,pbobject pbobj,LPCTSTR className,IPBX_VisualObject **obj)

{

PBXRESULT result = PBX_OK;

string cn(className);

if (cn.compare("visualext") == 0)

{

*obj = new CVisualExt(pbsession, pbobj);

}

else

{

*obj = NULL;

result = PBX_FAIL;

}

return PBX_OK;

};

在调用 CreateControl 之前,应该注册相应的窗口类,下面是一个注册代码,使用了

Windows 的 RegisterClass 方法,类名为 s_className。

void CVisualExt::RegisterClass()

{

WNDCLASS wndclass;

wndclass.style = CS_GLOBALCLASS|CS_DBLCLKS;

wndclass.lpfnWndProc = WindowProc;

wndclass.cbClsExtra = 0;

wndclass.cbWndExtra = 0;

wndclass.hInstance = g_dll_hModule;

wndclass.hIcon = NULL;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);

wndclass.hbrBackground =(HBRUSH) (COLOR_WINDOW + 1);

wndclass.lpszMenuName = NULL;

wndclass.lpszClassName = s_className;

::RegisterClass (&wndclass);

}

窗口关闭时,调用 UnregisterClass 方法实现 CreateControl。

void CVisualExt::UnregisterClass()

{

::UnregisterClass(s_className, g_dll_hModule);

}

通过 IPBX_VisualObject 的 GetClassName 方法,CreateControl 将类名传递给 Windows

第 11章 PBN I及第 3 方应用服务

— 313 —

的 CreateWindowEx 方法来创建一个窗口,最后返回这个窗口的句柄给 PBVM。

TCHAR CVisualExt::s_className[] = "PBVisualExt";

LPCTSTR CVisualExt::GetWindowClassName()

{

return s_className;

} HWND CVisualExt::CreateControl

(DWORD dwExStyle, // extended window style

LPCTSTR lpWindowName, // window name

DWORD dwStyle, // window style

int x, // horizontal position of window

int y, // vertical position of window

int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or

// owner window

HINSTANCE hInstance // handle to application

// instance

)

{

d_hwnd = CreateWindowEx(dwExStyle, s_className,

lpWindowName, dwStyle, x, y, nWidth, nHeight,

hWndParent, NULL, hInstance, NULL);

::SetWindowLong(d_hwnd, GWL_USERDATA, (LONG)this);

return d_hwnd;

}

11.2.3.6 创建 DLL 和 PBD

创建过程同非可视扩展创建过程的第 6 步。

11.2.3.7 在应用程序中使用可视化扩展

如第 5 步所述,在 PowerBuilder 应用中不需要声明可视类的实例,也不需要使用

CREATE 语句来创建实例,系统在驻留有可视扩展类的窗口打开时自动创建该类的实

例。

使用可视扩展类的方法如下。

· 从 PowerBuilder 主菜单中选择 File|Inherit,打开 Inherit from Object 对话框。

· 在“Libraries”文本列表框选择所用的 PBD 文件。

· 选定可视类,单击 OK 按钮。

· 在 User Object 画板中,调整可视对象并做出需要的修改。

· 存储对象。

11.2.4 在 C++应用程序中调用 PowerScript

为了调用已有的 PowerBuilder 应用,准确地说,是调用 PowerBuilder 对象里的一个

Pow erBuilder9.0 基础应用与系统开发

— 314 —

函数,必须把 PowerBuilder 虚拟机(PBVM)嵌入在 C++应用程序中。C++应用程序使用

Windows LoadLibrary 函数,通过载入动态链接库 pbvm90.dll 来载入 PBVM(虚拟机),接

着通过调用 PB_GetVM 函数(pbvm90.dll 导出的函数)获得一个指向接口 IPB_VM 的指针,

然后调用这个接口的 CreateSession 函数来创建一个会话(session)。

C++应用程序还可以进一步工作:创建某个 PowerBuilder 类的实例,并调用其函数,

当然,这一切都是通过 IPB_Session 接口进行的。

如图 11-1 反映了 C++应用程序和 PBVM 接口之间的关系。

C++

应用程序

PB_GetVM()

PBVM

IPB_VM

IPB_Session

IPB_Value

IPB_Argument

图 11-1 C++应用程序和 PBVM 接口之间的关系

11.2.5 C++调用 PowerBuilder 对象

C++调用 PowerBuilder 对象通常要经过以下几个步骤。

11.2.5.1 创建 PowerBuilder 对象

(1) 打开 PowerBuilder,新建一个 Workspace(工作区)。

(2) 打开 New 对话框的 Target 标签页,选中 Application 图标,新建一个应用,给这

个新建的 Application 起个名字,例如 loadpbni.pbl,然后单击 Finish 按钮。

(3) 打开 New 对话框的 PB Object 标签页,选中 Custom Class 图标,单击 OK 按

钮。

(4) 在函数原型窗口,用户可以根据自己的需要创建有同一参数和返回类型的函数,

例如用如下特征创建一个函数:

f_mult ( integer arg1, integer arg2 ) returns integer

(5) 选择 File|Save 存储用户对象,在弹出的窗口中为第 4 步创建的函数命名,例如

nvo_mult,然后关闭用户对象窗口。

第 11章 PBN I及第 3 方应用服务

— 315 —

11.2.5.2 获得函数签名

在编写调用函数之前,需要获得该被调用函数的 ID。这个方法的 ID 是初始化结构

PBCallInfo 所必需的。获得这个 ID,需要提供该方法的签名。

所谓的函数签名是一个字符串,这个字符串描述了它所代表的函数的返回类型和参数

信息,假如刚才所建立的函数有两个 int 型参数,返回类型也是 int 型,其签名就是字符

串“III”,表示这个函数返回一个 Integer 类型,并且需要两个 Integer 作为参数。下面介

绍用两种方法获得一个函数的签名。

通过 System Tree 区获得函数签名:打开 System Tree 区 loadpbvm.pbl 前的“+”号,

找到刚才所建立的用户对象 nvo_mult,打开其加号,右击“f_mult”函数,从弹出菜单中

选择“Property”,在弹出的对话框中提供了函数原型和函数签名。

使用工具 pbsig90 获得函数签名:在命令行模式中,键入如下信息:

pbsig90 G:/tmp/loadpbvm.pbl

在输出结果的最后一行中的符号“/*”和“*/”之间的“III”就是函数的签名,它被

传递给 GetMethodID 作为方法的 ID 参数。

11.2.5.3 创建 C++应用程序

创建一个 C++应用程序的详细步骤描述如下。

1. 装入 PowerBuilder 虚拟机(PBVM)

首先创建一个新的控制台应用项目。在此需要特别注意的是,Include 路径必须设置

为“PowerBuilder 9.0/SDK/PBNI/include”;Lib 路径必须包含“PBNI.lib”库文件。

下述 C++代码使用 PB_GetVM 创建一个 IPB_VM 对象,并且载入 PowerBuilder 虚

拟机。

#include �pbext.h” #include �stdio.h� typedef PBXEXPORT PBXRESULT(*P_PB_GetVM)(IPB_VM** vm); int main(int argc,char *argv[]) { IPB_Session* session; IPB_VM* pbvm=null; //载入 PowerBuilder 虚拟机模块 HINSTANCE hinst=LoadLibrary(�pbvm90.dll�); if(hinst==NULL) return 0; fprintf(stderr, �Loaded PBVM successfully\n�);

2. 调用 PB_GetVM 获得指向 IPB_VM 接口的指针

P_PB_GetVM getvm=(P_PB_GetVM)GetProcAddress(hinst,�PB_GetVM�) If(getvm==NULL) return 0; Getvm(&pbvm); If(pbvm==null) return 0;

Pow erBuilder9.0 基础应用与系统开发

— 316 —

3. 在 IPB_VM 中创建 IPB_Session 对象

// loadpbvm.pbl must contain an application object

// named loadpbvm and it must be on the search path

// for the executable file

LPCTSTR LibList[] = {"loadpbvm.pbl"};

if ( pbvm->CreateSession("loadpbvm", LibList, 1,

&session) != PBX_OK )

{

fprintf(stderr, "Error in CreateSession\n");

return 1;

}

fprintf(stderr, "Created session successfully\n");

4. 创建 PowerBuilder 对象的实例

// Create the PowerBuilder object contained

// in loadpbvm.pbl.

// First find the group that contains the

// user object nvo_mult

pbgroup group = session->FindGroup("nvo_mult",

pbgroup_userobject);

if (group == NULL) return 0;

// Now find the class nvo_mult in the group

pbclass cls = session->FindClass(group,"nvo_mult");

if (cls == NULL) return 0;

// Create an instance of the PowerBuilder object

pbobject pbobj = session->NewObject(cls);

5. 初始化 PBCallInfo 结构

要获得被调用函数的方法 ID,并且初始化 PBCallInfo 结构。获得一个函数的 ID

需要知道该函数的签名,至于其步骤,前面已有介绍,这里不再赘述。下面是具体实

现代码:

// PBCallInfo contains arguments and return value

PBCallInfo ci;

// To call the class member function f_mult,

// pass its signature as the last argument

// to GetMethodID

pbmethodID mid = session->GetMethodID(cls, "f_mult",PBRT_FUNCTION,

"III");

// Initialize call info structure based on method ID

session->InitCallInfo(cls, mid, &ci);

也可以使用 FindMatchingFunction 来代替 GetMethodID 获得方法 ID。其相应代码如

下:

pbmethodID mid = session->FindMatchingFunction(cls,"f_mult",

第 11章 PBN I及第 3 方应用服务

— 317 —

PBRT_FUNCTION, "int, int");

6. 调用 PowerBuilder 函数

调用函数之前,必须提供参数值。下面的代码实现在 PBCallInfo 结构里直接对它们

进行设置。

// Set IN arguments. The prototype of the function is

// integer f_mult(integer arg1, integer arg2)

ci.pArgs-> GetAt(0)->SetInt(123);

ci.pArgs-> GetAt(1)->SetInt(45);

最后调用函数。下面的代码采用 try-catch 结构来处理运行时的错误。

// Call the function

try

{

session->InvokeObjectFunction(pbobj, mid, &ci);

// Was PB exception thrown?

if (session->HasExceptionThrown())

{

// Handle PB exception

session->ClearException();

}

}

catch (...)

{

// Handle C++ exception

}

// Get the return value and print it to the console

pbint ret = ci.returnValue->GetInt();

fprintf(stderr, "The product of 123 and 45 is %i\n",ret);

7. 编写清除代码

在使用 PBCallInfo 结构完成各种操作之后,需要调用 FreeCallInfo 函数释放分配给该

结构的内存,删除相关结构,释放相应会话,并释放有关函数库。

// Release Call Info

session->FreeCallInfo(&ci);

delete &ci;

// Release session

session->Release();

return 0;

FreeLibrary(hinst);

}

11.2.5.4 运行 C++应用程序

编译、运行程序。如果 PowerBuilder VM 成功载入并且会话成功创建,就会输出如下

Pow erBuilder9.0 基础应用与系统开发

— 318 —

结果:

Loaded PBVM successfully

Created session successfully

The product of 123 and 45 is 5535

11.2.6 处理 PowerBuilder 消息

如 11.2.2 节所述,在 C++应用程序中可以打开 PowerBuilder 窗口,但是并没有确保

从 C++应用程序中处理 PowerBuilder 窗口中的控件。要做到这一点,就要用到 C++处理

PowerBuilder 消息的功能。PBNI 利用 IPB_Session 接口中的 ProcessPBMessage()函数来实

现此功能。

ProcessPBMessage()函数与 Yield()函数的功能类似,从队列中的 PowerBuilder 对象或

其 他 图 形 对 象 中 获 取 消 息 , 并 产 生 对 其 他 图 形 对 象 的 控 制 信 息 。 但 是 ,

ProcessPBMessage()与 Yield()所不同的是,ProcessPBMessage 函数每次检索 PowerBuilder

消息队列,一次只能处理一个消息,而且只能处理 PowerBuilder 消息。

通过调用 PostEvent 函数将消息加入到消息队列中。

程序中必须保证重复调用 ProcessPBMessage()函数。对于大多数 C++应用程序,可以

在主函数中,设置一个消息循环,并将 ProcessPBMessage()插入到循环体。下面的例子中

包含有这一部分代码。

但是,如果使用的是 MFC 编写 C++应用程序,我们是不可能修改其内建的消息循环

的 。 这 种 情 况 下 , 为 确 保 ProcessPBMessage() 函 数 被 重 复 调 用 , 需 要 重 载

CWnd::WindowProc 函数并将 ProcessPBMessage()插入重载的函数中:

LRESULT CCallPBVCtrl::WindowProc(UINT message,

WPARAM wParam, LPARAM lParam)

{

d_session->ProcessPBMessage();

return CDialog::WindowProc(message, wParam, lParam);

}

11.3 引导扩展�PB 与 Java 及第 3 方服务的中介

PowerBuilder 应用不能直接操作 Java 类,因此二者之间的通信要通过一个中介——

Marshaler Extension(引导扩展)。顾名思义,该引导扩展起到一个引导的作用,其作为一架

桥梁沟通了 PowerBuilder 与 Java 以及其他 PowerBuilder 所不能直接访问的组件。

11.3.1 关于引导扩展

一个引导扩展通常是提供一个充当创建器的本地类。该类定义了一个可创建外部组件

的函数(1),外部组件(2)由函数的参数指定。如果该函数成功创建了一个外部组件的实

例,接着它通过 PBVM 就为这个实例创建一个代理(3,4),然后再创建一个引导对象,并

将这个引导对象与代理联系在一起。图 11-2 展示了其工作原理。

第 11章 PBN I及第 3 方应用服务

— 319 —

PBVM

(PB 虚拟机)

Creator

本地类

PROXY

(代理)

Foreign Component

外部组件

Marshaler

引导对象

1

3,6 2

54

图 11-2 引导扩展的工作原理

完成上述创建过程后,用户就可调用外部组件的方法。这种调用首先是通过对代理对

象的函数调用开始的,PBVM 通过代理调用引导对象的 InvokeRemoteMethod 方法。然

后,引导对象将 PowerBuilder 函数调用翻译成外部组件所能理解的请求并发送,接收到

外部组件的响应之后,再将结果返回 PowerBuilder。图 11-3 是其调用原理。

PBVM

(PB 虚拟机)

PROXY

(代理)

Foreign Component

外部组件

Marshaler

引导对象

1

3 2

4

4 4

图 11-3 引导扩展的方法调用原理

11.3.2 开发引导扩展

开发引导扩展的具体步骤如下。

11.3.2.1 描述扩展集的特征

前面讲述非可视扩展及导出 PBX_GetDescription 方法时已经给出了其方法的具体实

现代码,下面将给出其中引导扩展中的实现代码。

本例中,设计了一个称为 CJavaVM 的创建器,该创建器继承于 NonVisulObject 类,

并具有两个方法:CreateJavaObject 和 CreateJavaVM。

PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()

{

static const TCHAR desc[] = {

"class javavm from nonvisualobject\n"

"function long createjavavm(string classpath,string properties[])\n"

"function long createjavaobject(ref powerobjectproxyobject, readonly

Pow erBuilder9.0 基础应用与系统开发

— 320 —

string javaclassname,readonly string proxyname)\n"

"end class\n"

};

return desc;

}

11.3.2.2 实现创建器

同创建非可视本地类相同,除了本身所具有的方法,引导扩展的创建器还必须实现

Invoke 和 Destroy 方法。

11.3.2.3 实现引导扩展类

实现了创建器类之后,就要实现引导类,有关实现方法与实现非可视扩展基本相同,

惟一的区别就是以 InvokeRemoteMethod 方法代替 Invoke 方法。

11.3.3 为 Java 类创建 PowerBuilder 代理

如果想从 PowerBuilder 中调用 Java 类,就必须为所要调用的 Java 类创建一个代理。

创建代理可以采用以下两种方式。

(1) 利用 Java 映像,直接从 Java 源码中创建。

(2) 利用 javap 工具。

例如,要调用如下 Java 类。

public class Converter

{

public double dollarToYen(double dollar);

public double yenToEuro(double yen);

}

该类的代理应该保存为 convert.srx 文件,内容如下:

$PBExportHeader$converter.srx

$PBExportComments$Proxy generated for Java class

global type Converter from nonvisualobject

end type

global Converter Converter

forward prototypes

public:

function double dollarToYen(double ad_1) alias

for "dollarToYen,(D)D"

function double yenToEuro(double ad_1) alias

for "yenToEuro,(D)D"

end prototypes

11.3.4 从 PowerBuilder 中调用 Java 类

在 PowerBuilder 中调用 Java 类之前首先要将代理加入到“PowerScript target”中,在

第 11章 PBN I及第 3 方应用服务

— 321 —

System tree 区选中要存储代理的库右击,选择 Import 按钮,将 converer.srx 文件导入。

从窗口的 Open 事件中,创建一个 Java VM:

// instance variable: javavm i_jvm

string properties[]

i_jvm = create javavm

string classpath

i_jvm.createjavavm(classpath, properties)

在窗口中添加一个按钮,并在其 Clicked 事件中使用 JavaVM 的 CreateJavaObject 方

法创建一个 Convert 对象,并调用其相应方法。

converter conv

double yen

i_jvm.createjavaobject(conv, "Converter", "converter")

yen = conv.dollarToYen(100.0)

messagebox("Yen", string(yen))

利用 PowerScript 调用 CreateJavaObject 方法时,PBVM 调用扩展中相应的 C++方

法,后者通过 JNI 创建一个 Java Convert 对象。如果调用成功,该方法创建一个 PB

Convert 代理的实例和一个 Java 引导对象,并将二者联系在一起。

在执行到 conv.dollarToYen(100.0)语句时,PBVM 调用 InvokeRemoteMethod 方法,该

方法通过 JNI 又转发给 JAVA Convert 对象,并将结果返回 PowerBuilder。

11.4 PBNI 应用实例

本节实例均来自 sybase 公司网站。http://www.sybase.com/pb9_samples,更多实例和

信息请参阅 PowerBuilder 9.0 用户手册。

11.4.1 非可视扩展实例——Ping

本例是一个对非可视扩展集的实现,它封装了 CPing。下面的代码是 PBNI_Ping.cpp

文件中的一部分,表达了 PBNI 扩展如何调用 MFC 的 CPing 类。

CPING_USE_WINSOCK2

{

CPing p1;

CPingReply pr1;

BOOL pingcode;

pingcode=p1.Ping2(hostname, pr1);

my_error=GetLastError();

if (pingcode)

{

hostent* phostent = gethostbyaddr((char *)&pr1.Address.S_un.S_addr,

4, PF_INET);

Pow erBuilder9.0 基础应用与系统开发

— 322 —

// get the array of address arguments

IPB_Value* pArg1 = ci->pArgs->GetAt(1);

// We pass 4 elements in the array, but just for demonstration of

ArrayInfo

if (!pArg1->IsNull())

{

pbarray array = pArg1->GetArray();

PBArrayInfo *arrayInfo = session->GetArrayInfo(array);

if( arrayInfo->arrayType == PBArrayInfo::UnboundedArray

|| arrayInfo->numDimensions == 1)

{

PBArrayInfo::ArrayBound arrayBounds;

pblong dim[1];

if( arrayInfo->arrayType == PBArrayInfo::UnboundedArray )

{

arrayBounds.lowerBound = 1;

arrayBounds.upperBound = session->GetArrayLength(array);

}

else

arrayBounds = arrayInfo->bounds[0];

pblong size = arrayBounds.upperBound - arrayBounds.lowerBound + 1;

long* longPtr = new long[size];

pbint j = 0;

pbboolean isNull = false;

// We pass 4 elements in the array, but just for demonstration

// Setting the values in the array [0] to [3] with s_b1 to s_b4

from the address structure

for (pblong i = arrayBounds.lowerBound; i <=

arrayBounds.upperBound;)

{

dim[0] = i;

session->SetLongArrayItem(array, dim,

pr1.Address.S_un.S_un_b.s_b1);

i++;

dim[0] = i;

session->SetLongArrayItem(array, dim,

pr1.Address.S_un.S_un_b.s_b2);

i++;

dim[0] = i;

session->SetLongArrayItem(array, dim,

pr1.Address.S_un.S_un_b.s_b3);

i++;

dim[0] = i;

session->SetLongArrayItem(array, dim,

pr1.Address.S_un.S_un_b.s_b4);

i++;

}

session->ReleaseArrayInfo(arrayInfo);

ci-> pArgs -> GetAt(1) -> SetArray(array);

第 11章 PBN I及第 3 方应用服务

— 323 —

}

}

//other arguments values (passed in by reference)

ci-> pArgs -> GetAt(2) -> SetString(phostent->h_name);

ci-> pArgs -> GetAt(3) -> SetLong(pr1.RTT);

ci-> pArgs -> GetAt(4) -> SetUlong(my_error);

}

else //the ping was errenous - false

{

//Set the values for output

IPB_Value* pArg1 = ci->pArgs->GetAt(1);

pbarray array = pArg1->GetArray();

//Array will be set to zeros..

ci-> pArgs -> GetAt(1) -> SetArray(array);

ci-> pArgs -> GetAt(2) -> SetString("");

ci-> pArgs -> GetAt(3) -> SetLong(0);

ci-> pArgs -> GetAt(4) -> SetUlong(my_error);

}

}

11.4.2 引导扩展实例——从 PowerBuilder 中调用 JAVA

本例在 11.3 中已经提到,引导扩展中声明了一个本地类——JavaVM,其包括两个方

法:createJavaVM 和 createJavaObject。

Java 的 Convert 类实现货币换算的功能,按照不同的汇率实现美元和欧元等之间的换

算,本节只列出与本章内容有关的部分代码:

#include <windows.h>

#include <pbext.h>

#include "CJavaVM.h"

#include <string>

using std::string;

pbulong g_dll_hModule = 0;

BOOL APIENTRY DllMain( HANDLE hModule,

DWORD reasonForCall,

LPVOID lpReserved)

{

g_dll_hModule = (pbulong)hModule;

switch (reasonForCall)

{

case DLL_PROCESS_ATTACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

Pow erBuilder9.0 基础应用与系统开发

— 324 —

PBXEXPORT LPCTSTR PBXCALL PBX_GetDescription()

{

static const TCHAR desc[] = {

"class javavm from nonvisualobject\n"

"function long createjavavm(string classpath, string

properties[])\n"

"function long createjavaobject(ref powerobject proxyobject,

readonly string javaclassname, readonly string proxyname)\n"

"end class\n"

};

return desc;

}

PBXEXPORT PBXRESULT PBXCALL PBX_CreateNonVisualObject

( IPB_Session* pbsession,

pbobject pbobj,

LPCTSTR className,

IPBX_NonVisualObject **obj)

{

PBXRESULT result = PBX_OK;

string cn(className);

if (cn.compare("javavm") == 0)

{

*obj = new CJavaVM(pbobj);

}

else

{

*obj = NULL;

result = PBX_E_NO_SUCH_CLASS;

}

return PBX_OK;

};

CJavaVM.cpp——创建类的实现(省去构造函数和析构函数)

enum MethodIDs

{

mCreateJavaVM = 0,

mCreateJavaObject

};

PBXRESULT CJavaVM::Invoke

(

IPB_Session *session,

pbobject obj,

pbmethodID mid,

PBCallInfo *ci

)

{

PBXRESULT result = PBX_OK;

switch (mid)

{

case mCreateJavaVM:

result = CreateJavaVM(session, obj, ci);

第 11章 PBN I及第 3 方应用服务

— 325 —

break;

case mCreateJavaObject:

result = CreateJavaObject(session, obj, ci);

break;

default:

result = PBX_E_INVOKE_FAILURE;

break;

}

return result;

}

PBXRESULT CJavaVM::CreateJavaVM

(

IPB_Session *session,

pbobject obj,

PBCallInfo *ci

)

{

enum

{

kSuccessful = 0,

kFailedToLoadJVM = -1,

kCannotFindEJBLocator = -2

};

pblong result = kSuccessful;

LPCTSTR classPath = NULL;

IPB_Value* pArg0 = ci->pArgs->GetAt(0);

if (!pArg0->IsNull())

{

pbstring t = pArg0->GetString();

if (t != NULL)

classPath = session->GetString(t);

}

LPCTSTR* properties = NULL;

IPB_Value* pArg1 = ci->pArgs->GetAt(1);

if (!pArg1->IsNull())

{

pbarray array = pArg1->GetArray();

PBArrayInfo *arrayInfo = session->GetArrayInfo(array);

if( arrayInfo->arrayType == PBArrayInfo::UnboundedArray

|| arrayInfo->numDimensions == 1)

{

PBArrayInfo::ArrayBound arrayBounds;

pblong dim[1];

if( arrayInfo->arrayType == PBArrayInfo::UnboundedArray )

{

arrayBounds.lowerBound = 1;

arrayBounds.upperBound = session->GetArrayLength(array);

}

else

arrayBounds = arrayInfo->bounds[0];

Pow erBuilder9.0 基础应用与系统开发

— 326 —

pblong size = arrayBounds.upperBound - arrayBounds.lowerBound + 1;

LPCTSTR* strPtr = new LPCTSTR[size];

pbint j = 0;

pbboolean isNull = false;

for (pblong i = arrayBounds.lowerBound; i <=

arrayBounds.upperBound; i++,j++)

{

dim[0] = i;

pbstring pbstr = session->GetStringArrayItem(array, dim,

isNull);

if (pbstr != NULL)

{

strPtr[j] = session->GetString(pbstr);

}

}

properties = strPtr;

}

session->ReleaseArrayInfo(arrayInfo);

}

JavaVMWrapper* jvm = JavaVMWrapper::instance();

if (!jvm->loadJavaVM(classPath, properties))

{

result = kFailedToLoadJVM;

}

if ( properties != NULL) //delete properties memeory.

{

delete []properties;

}

ci->returnValue->SetLong(result);

return PBX_OK;

}

PBXRESULT CJavaVM::CreateJavaObject

(

IPB_Session *session,

pbobject obj,

PBCallInfo *ci

)

{

enum

{

kSuccessful = 0,

kInvalidJavaClassName = -1,

kFailedToCreateJavaClass = -2,

kInvalidProxyName = -3,

kFailToCreateProxy = -4

};

// Get java class name.

string jclassName;

{

第 11章 PBN I及第 3 方应用服务

— 327 —

pbstring jcn = ci->pArgs->GetAt(1)->GetString();

if (jcn == NULL)

{

ci->returnValue->SetLong(kInvalidJavaClassName);

return PBX_OK;

}

else

{

jclassName = session->GetString(jcn);

}

}

// Create java object

JavaVMWrapper* jvm = JavaVMWrapper::instance();

JNIEnv* env = jvm->getEnv();

jclass jcls = env->FindClass(jclassName.c_str());

jobject jobj = NULL;

if (jcls != NULL)

{

JLocalRef lrClz(env, jcls);

jmethodID mid = env->GetMethodID(jcls, "<init>", "()V");

if (mid != NULL)

{

jobj = env->NewObject(jcls, mid);

}

}

// Get PB proxy name

string proxyName;

{

pbstring pn = ci->pArgs->GetAt(2)->GetString();

if (pn == NULL)

{

ci->returnValue->SetLong(kInvalidProxyName);

return PBX_OK;

}

else

{

proxyName = session->GetString(pn);

}

}

// Find proxy class

pbgroup group = session->FindGroup(proxyName.c_str(), pbgroup_proxy);

if (group == NULL)

{

ci->returnValue->SetLong(kInvalidProxyName);

return PBX_OK;

}

pbclass cls = session->FindClass(group, proxyName.c_str());

if (cls == NULL)

{

ci->returnValue->SetLong(kInvalidProxyName);

Pow erBuilder9.0 基础应用与系统开发

— 328 —

return PBX_OK;

}

// Create PB proxy object.

pbproxyObject proxy = session->NewProxyObject(cls);

if (proxy == NULL)

{

ci->returnValue->SetLong(kFailToCreateProxy);

return PBX_OK;

}

// Create JavaMarshaler

JavaMarshaler* marshaler = new JavaMarshaler(env, proxy, jobj);

// Associate the JavaMarshaler with the PB proxy

session->SetMarshaler(proxy, marshaler);

ci->pArgs->GetAt(0)->SetObject(proxy);

ci->returnValue->SetLong(kSuccessful);

return PBX_OK;

}

void CJavaVM::Destroy()

{

delete this;

}

11.5 练习题

(1) 简述创建可视扩展类的实例与一个不可视扩展类实例有什么区别,并举例说明。

(2) 请解释引导扩展的工作原理。

(3) 请解释下面一段代码,简要描述每一句的功能。

pbclass cls;

pbmethodID mid;

PBCallInfo* ci = new PBCallInfo;

cls = Session -> GetClass(myobj);

mid = Session -> GetMethodID(cls, "myfunc",PBRT_FUNCTION, "II");

Session -> InitCallInfo(cls, mid, ci);

(4) 某公司的人事管理系统是用 JAVA 编写的,其中定义了雇员类(class employee),

封装了加薪方法(addSalary),并且需要一个参数雇员 ID,返回值类型为布尔型。当某一个

雇 员 符 合 加 薪 要 求 时 , 该 方 法 返 回 true , 否 则 返 回 false 。 公 司 新 领 导 决 定 使 用

PowerBuilder 9.0 重新设计系统,请为该公司设计一个系统,直接调用原雇员类的有关方

法。

第 12 章 文档对象模型

PBDOM 是文档对象模型的 PowerBuilder 处理工具,是通过 XML 文档可访问和操控

的程序接口。事实上,PBDOM 与 W3C DOM API 工具很相似,PBDOM PowerBuilder API

可以从 Powerscript 源码中读取、编辑并操作标准格式的 XML。PBDOM 把 XML 文档看

作互连对象的集合,并提供直观方式标识每个对象的用途和功能。PBDOM 和面向 XML

文件的 Java 基本文档对象模型 JDOM 也很类似。

PBDOM 与 XML 文档的相互作用与由父节点和子节点共同构成的树形结构模型一

致。一个文档元素代表一个独立的 XML 文档的最高层节点,这个元素有一个或多个子节

点,用户可以通过合适的类程序找到树中的节点。

PBDOM XML 分析器常用于存储和分析 XML 文档,同时产生用户指定 DOM 结点的

XML。该分析器能够遍历节点树,访问节点及其属性值,插入和删除节点,以及序列化

节点树后又返回到 XML。

下一节将对 PBDOM 对象层进行详细解释,并对每一对象的方法做具体讲解。

12.1 PBDOM 对象

PBDOM_OBJECT 继承于 PBDOM 非可视化对象类,用来表示 XML 节点,是

PBDOM 对象的基类。

PBDOM 通过下列类代表节点类型。

· 属性

· CDATA 段

· 字符数据

· 注释

· 文档类型

· 文档

· 元素

· 实体引用

· 操作指令

· 文本

可以从以上类中选取合适的方法访问 PBDOM 节点树中的对象。

PBDOM_BUILDER 类不能表示 DOM 节点,但可用于创建基于 XML 的 PBDOM 对

象树。该类继承于 PowerBuilder 非可视化对象类。

PBDOM_EXCEPTION 类继承于 PowerBuilder 异常类,并提供了获得错误码的方法。

表 12-1 所示的 W3C DOM 和 JDOM 对象,相当于 DOM 树节点代表的每一个

PBDOM 对象。注意:两者并不完全相同。

Pow erBuilder9.0 基础应用与系统开发

— 330 —

表 12-1 对应于 PBDOM 对象的 W3C DOM 和 JDOM 对象

PBDOM W3CDOM JDOM PBDOM 属性 属性节点 属性

PBDOM 生成器 无 DOM 生成器

PBDOM CDATA 段 CDATA 段节点 CDATA 段

PBDOM 字符数据 字符数据节点 无

PBDOM 注释 注释节点 注释

PBDOM 文档 文档节点 文档

PBDOM 文档类型 文档类型节点 文档类型

PBDOM 元素 元素节点 元素

PBDOM 实体引用 实体引用节点 实体引用

PBDOM 对象 节点 无

PBDOM 操作指令 操作指令节点 操作指令

PBDOM 文本 文本节点 文本

对象分级:PBDOM 对象层不同于 W3C 和 JDOM 的对象层,如图 12-1 所示。

PBDOM 操作指令

PBDOM 元素

PBDOM 实体引用

PBDOM 文档

PBDOM 文档类型

PBDOM 字符数据

PBDOM 属性

PBDOM 对象

PBDOM 字符数据

PBDOM 文本 PBDOM CDATA 段

PBDOM 生成器

PBDOM 异常

图 12-1 PBDOM 对象层

下面,将对图中的对象一一进行介绍。

12.2 PBDOM_Attribute 对象

12.2.1 定义

PBDOM_Attribute 类是对由 PowerScript 语言建模的 XML 属性行为的定义。通过该

方法可以获得属性值。

PBDOM_Attribute 类 包 含 了 子 类 PBDOM_OBJECTS 的 一 个子 树 , 这 些子 类 是

PBDOM 文本和实体引用对象的结合。

第 12 章 文档对象模型

— 331 —

PBDOM_Attribute 没有父节点,不过,该对象有一个 PowerBuilder 元素主体,用户可

以通过 GetOwnerElementObject 方法获得该对象,通过 SetOwnerElementObject 方法建立

其主体。

12.2.2 方法

客观上说,从 PBDOM_Object 中继承的部分方法并没有实际的意义,这些方法只是

返回了默认值或默认的函数结果,这些方法在表 12-2 中给出了具体的说明。

表 12-2 PBDOM_Attribute 类特殊方法

方 法 返回值 GetParentObject Null 空值 SetParentObject 当前的 PBDOM 属性,返回的未修改的值作为 PBDOM 对象

除了表 12-2 给出的方法外,PBDOM 属性还有见表 12-3 所述的重要方法。

表 12-3 PBDOM_Attribute 类常用方法

方 法 描 述 AddContent 添加输入的 PBDOM 对象作为 PBDOM 属性的子类节点 Clone 创建一个 PBDOM 属性对象的复制品 Detach 从其 PBDOM 对象主体上取一个 PBDOM 属性,是一个 PBDOM 元素 Equals 根据所提供方法对 PBDOM 对象和 PBDOM 属性作等价测试 GetBolleanValue 从布尔形式中获得 PBDOM 属性值

GetContent 返回一个 PBDOM_OBJECT 对象,该对象为 PBDOM 属性的子类,PBDOM 属性的子类尽可能是

PBDOM 文本和实体对象 GetDateValue 返回 PBDOM 属性对象值作为数据类型 GetDateTimeValue 返回 PBDOM 属性对象值作为数据时间类型 GetDoubleValue 以双精度形式返回 PBDOM 属性对象值 GetIntValue 返回 PBDOM 属性对象作为整形类型 GetLongValue 返回 PBDOM 属性对象作为长整形 GetNamespacePrefix 恢复 PBDOM 属性对象的逻辑名 GetNamespaceUri 获得 PBDOM 属性对象的名字间隔前缀,当 PBDOM 属性无名字间隔时,该方法返回空串 GetObjectClass 返回一个长整形代码来说明当前 PBDOM 对象类 GetObjectClassString 返回一个 PBDOM 对象类的串形式 GetOwnerDocumentObject 返回具有 PBDOM 属性的 PBDOM 文档对象 GetOwnerElenentObject 返回这个 PBDOM 属性的主体 PBDOM 元素,若无主体元素,返回空值 GetQualifiedName 获得一个 PBDOM 属性的限定名,该方法返回 PBDOM 属性的无名字间隔的局部名字 GetRealValue 返回一个 PBDOM 属性对象制作为实数类型 GetText 返回一个 PBDOM 属性对象的文本值

GetTextNormalize 返回包含在 PBDOM 属性对象中的文本数据,同时删除周围的白间隔字符,并以单间距取代内部

的白间隔字符 GetTextTrim 返回 PBDOM 属性对象中的文本数据,同时删除周围间隔 GetUintValue 返回 PBDOM 属性对象值作为时间类型 GetTimeValue 返回 PBDOM 属性对象值作为非整形类型 GetUlongValue 返回 PBDOM 属性对象值作为非长形类型 HasChildren 确定 PBDOM 属性对象是否还有任何 PBDOM 对象子类 InsertContent 通过参照的 PBDOM 对象特定位置插入一个 PBDOM 对象作为 PBDOM 属性的子节点 IsAncestorObjectOf 确定当前的 PBDOM 属性对象是否是其他 PBDOM 对象的祖先 RemoveContent 从 PBDOM 属性中删除输入的 PBDOM 对象 SetBooleanValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的布尔值使之成串来构造文本值 SetContent 设置 PBDOM 属性内容 SetDateValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的日期值成串来创建文本值 SetDateTimeValue 设置 PBDOM 属性对象的时间日期值,该方法通过排列所提供的时间日期值成串来创建文本值 SetDoubleValue 设置 PBDOM 属性对象双精度值,该方法通过排列所提供的双精度值成串来创建文本值

Pow erBuilder9.0 基础应用与系统开发

— 332 —

(续表)

方 法 描 述 SetIntValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的整型值成串来创建文本值 SetLongValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的长型值成串来创建文本值 SetName 设置 PBDOM 属性对象的逻辑名 SetNamespace 在制定名字空间前缀和 URI 基础上设置 PBDOM 属性对象的名字间隔 SetOwnerElementObject 设置输入的 PBDOM 元素作为当前的 PBDOM 属性主体 SetRealValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的实型值成串来创建文本值 SetText 设置 PBDOM 属性对象的串值 SetTimeValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的时间值成串来创建文本值 SetUintValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的非整型值成串来创建文本值 SetUlongValue 设置 PBDOM 属性对象文本值,该方法通过排列所提供的非长型值成串来创建文本值

这 里 给 出 一 例 , 通 过 串 <abc My_Attr="An Attribute"/> 创 建 一 个

PBDOM_DOCUMENT,从根元素得到其属性,并从中创建一个浅层副本和深层副本。对

浅层副本,在信息框中返回一个空串。对深层副本,An Attribute 串被返回:

PBDOM_BUILDER pbdom_buildr

PBDOM_DOCUMENT pbdom_doc

PBDOM_ATTRIBUTE pbdom_attr

PBDOM_ATTRIBUTE pbdom_attr_clone_deep

PBDOM_ATTRIBUTE pbdom_attr_clone_shallow

string strXML = "<abc My_Attr=~"An Attribute~"/>"

TRY

pbdom_buildr = Create PBDOM_BUILDER

pbdom_doc = pbdom_buildr.BuildFromString(strXML)

pbdom_attr = pbdom_doc.GetRootElement(). &

GetAttribute("My_Attr")

pbdom_attr_clone_shallow = pbdom_attr.Clone(false)

MessageBox ("Shallow Attribute Clone Text", &

pbdom_attr_clone_shallow.GetText())

pbdom_attr_clone_deep = pbdom_attr.Clone(true)

MessageBox ("Deep Attribute Clone Text", &

pbdom_attr_clone_deep.GetText())

CATCH (PBDOM_EXCEPTION pbdom_except)

MessageBox ("PBDOM_EXCEPTION", &

pbdom_except.GetMessage())

END TRY

12.3 PBDOM_Builder 对象

12.3.1 PBDOM_Builder 对象定义

PBDOM 生成器类作为一个 DOM 制造工厂,从不同的输入资源中创建一个 PBDOM

文档,例如串和数据存储等。PBDOM 生成器类并非一个 PBDOM 对象,进行具体处理时

也不会遇到 DOM 对象。

与 PBDOM 文档的 NewDocument 方法相对而言,PBDOM 生成器方法常用来从头建

立一个 PBDOM 文档。

第 12 章 文档对象模型

— 333 —

12.3.2 PBDOM_Builder 对象方法

PBDOM_Builder 方法见表 12-4。

表 12-4 PBDOM_Builder 类方法

方 法 描 述 BuildFromDataStore 在参考的数据存储对象中创建 PBDOM 文档 BuildFromFile 在输入 URL 串指定文件中创建 PBDOM 文档 BuildFromString 从串中创建一个 PBDOM 文档 GetParseErrors 获得在文档分析过程中检测到的错误分析表

下面给出一段 Powerscript 代码,来说明如何将 BuildFromDataStore 方法作用于一个

数据存储对象上。

PBDOM_Builder pbdom_bldr

pbdom_document pbdom_doc

datastore ds

ds = Create datastore

ds.DataObject = "d_customer"

ds.SetTransObject (SQLCA)

ds.Retrieve()

pbdom_doc = pbdom_bldr.BuildFromDataStore(ds)

本例中,首先创建数据存储对象 ds,与数据放在一起,对 ds 使用 BuildFromDataStore

方法。BuildFromDataStore 方法使 ds 向 XML 输出数据,并对 ds 用到了绝大多数当前的

XML 样本,而后再用 XML 建立一个 PBDOM_DOCUMENT。PBDOM_DOCUMENT 对

象被指定到 pbdom_doc。

12.4 PBDOM_CDATA 对象

12.4.1 PBDOM_CDATA 对象定义

PBDOM_CDATA 类 代 表 了 一 个 XML DOM CDATA 段 , 该 类 继 承 于

PBDOM_CHARACTERDATA 类属性。

PBDOM_CDATA 对象通常用来保存含有文本对象禁止属性的文本,例如“<”和

“&”,这些文本没用到实体引用。下面给出一个简单的实例进行说明。

<some_text>

<![CDATA[ (x < y) & (y < z) => x < z ]]>

</some_text>

具有相同文本内容的 PBDOM 文本对象可表述如下:

<some_text>

(x &lt; y) &amp; (y &lt; z) =&gt; x &lt; z

</some_text>

Pow erBuilder9.0 基础应用与系统开发

— 334 —

然而,尽管 PBDOM_CDATA 类是从 PBDOM_TEXT 得到的,PBDOM_CDATA 对象

不能总像 PBDOM 文本一样在相同的状态下插入。举例来说,PBDOM 文本对象可以像

PBDOM 属性子类一样被添加,但 PBDOM_CDATA 对象不能。

12.4.2 PBDOM_CDATA 对象方法

客观上说,从 PBDOM_Object 中继承的部分方法并没有实际的意义,这些方法只是

返回了默认值或默认的函数结果,这些方法见表 12-5。

表 12-5 PBDOM_CDATA 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_CDATA GetContent False GetName 字符串“#data” HasChildren False InsertContent 当前的 PBDOM_CDATA IsAncestorObjectOf False RomoveContent False SetContent 当前的 PBDOM_CDATA SetName False

PBDOM_CDATA 类有所列重要方法,见表 12-6。

表 12-6 PBDOM_CDATA 类常用方法

方 法 描 述 Append 向文本内容中追加输入串或输入 PBDOM 字符数据对象的文本数据,该对象已存在于当前的

PBDOM_CDATA 对象中 Clone 创建和返回当前的 PBDOM_CDATA 复制品 Detach 从父节点 PBDOM 对象上取一个 PBDOM_CDATA Equals 对当前的 PBDOM_CDATA 和 PBDOM_OBJECT 引用等价性的测试 GetObjectClass 返回一个长整形代码,指明当前的 PBDOM 对象类 GetObjectClassString 返回一个 PBDOM 对象类的字串形式 GetOwnerDocumentObject 返回当前 PBDOM_CDATA 自己的 PBDOM 文档 GetParentObject 返回当前 PBDOM_CDATA 的 PBDOM 对象父节点,如果无,返回 null GetText 返回包含在当前 PBDOM_CDATA 对象中的文本数据 GetTextNormalize 返回包含在当前 PBDOM_CDATA 对象中的文本数据,删除周围的白间隔字符,对内部的白间隔

字符使之标准化为单倍行距 GetTextTrim 返回当前的 PBDOM_CDATA 对象的原文内容,删除周围的白间隔字符 SetParentObject 设置引用 PBDOM 对象为当前的 PBDOM_CDATA SetText 设置输入串为当前 PBDOM_CDATA 对象的文本内容

下面,给出一个使用 Clone 方法的例子。该例测试了一个 PBDOM_CDATA 对象

Clone 副本的下列特性。

(1) 原 PBDOM_CDATA 对象内容与 Clone 对象是否完全一致。

(2) 一个 PBDOM_CDATA 副本初始无父类对象。

(3) 一个 PBDOM_CDATA 副本初始的主体文档与原对象相一致。

PBDOM_BUILDER pbdom_buildr

PBDOM_DOCUMENT pbdom_doc

PBDOM_CDATA pbdom_cdat

PBDOM_OBJECT pbdom_obj_array[]

第 12 章 文档对象模型

— 335 —

string strXML = "<!DOCTYPE root [<!ELEMENT root

(#PCDATA)>]><root><![CDATA[This is a CDATA Section.]]></root>"

try

// Build a PBDOM_DOCUMENT based on strXML.

pbdom_buildr = Create PBDOM_BUILDER

pbdom_doc = pbdom_buildr.BuildFromString (strXML)

// Get the contents of the root element.

pbdom_doc.GetRootElement().GetContent(pbdom_obj_array)

// Test if the root element contains only one child object.

if (UpperBound(pbdom_obj_array) = 1) then

MessageBox ("Pass", "Root Element has only one child.")

else

MessageBox ("Fail", "Root Element must have only one child.")

end if

// Make a clone of the only child of the root element.

pbdom_cdat = pbdom_obj_array[1].Clone(true)

// Test if the clone is a PBDOM_CDATA object.

if (pbdom_cdat.GetObjectClassString() = "pbdom_cdata") then

MessageBox ("Pass", &

"The first child, after being cloned, is indeed a PBDOM_CDATA object.")

else

MessageBox ("Fail", "The first child, after being cloned, " &

+ "is found to be a " + pbdom_cdat.GetObjectClassString() + " object.")

end if

// Test if the clone is a CDATA section.

if (pbdom_cdat.GetText() = "This is a CDATA Section.") then

MessageBox ("Pass", "The text contents of the clone is correct.")

else

MessageBox ("Fail", "The text contents of the clone is : [" &

+ pbdom_cdat.GetText() + "]. This is incorrect.")

end if

// Test that the clone has no parent.

if (Not IsValid(pbdom_cdat.GetParentObject())) then

MessageBox ("Pass", "The clone has no parent.")

else

12.5 PBDOM_CharacterData 对象

12.5.1 PBDOM_CharacterData 对象定义

PBDOM_CHARACTERDATA 类代表了在 XML 文档中以字符为基础的无标记内容,

该类扩展了 PBDOM_OBJECT 类中专用于操作 DOM 的字符数据的一系列方法。

PBDOM_CHARACTERDATA 类是下列 3 种 PBDOM 类的父类。

(1) PBDOM_TEXT

(2) PBDOM_CDATA

Pow erBuilder9.0 基础应用与系统开发

— 336 —

(3) PBDOM_COMMENT

PBDOM_CHARACTERDATA 类和其父类 PBDOM_OBJECT 一样,是个“虚类”(与

VC++类近似),不能直接用例子说明。

举例来说,下述代码实现建立一个 pbdom_chrdata 文本以引发异常。

PBDOM_CHARACTERDATA pbdom_chrdata

pbdom_chrdata = CREATE PBDOM_CHARACTERDATA

pbdom_chrdata.SetText ("character string")//error

下例中,pbdom_chrdata 作为 PBDOM_CHARACTERDATA 类进行说明,又作为

PBDOM_TEXT 类举例说明,因此能够顺利创建 pbdom_chrdata 文本。

PBDOM_CHARACTERDATA pbdom_chrdata

pbdom_chrdata = CREATE PBDOM_TEXT

pbdom_chrdata.SetText ("character string")//success

12.5.2 PBDOM_CharacterData 对象方法

客观上说,从 PBDOM_Object 中继承的部分方法并没有实际的意义,这些方法只是

返回了默认值或默认的函数结果,这些方法见表 12-7。

表 12-7 PBDOM_CharacterData 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_CHARACTERDATA GetContent False InsertContent 当前的 PBDOM_CHARACTERDATA RemoveContent False SetContent 当前的 PBDOM_CHARACTERDATA SetName False

PBDOM_CHARACTERDATA 具有的方法,见表 12-8。

表 12-8 PBDOM_CharacterData 类常用方法

方 法 描 述 Append 多语法调用,通过语法一二实现 Detach 从父类对象上取一个 PBDOM 字符数据 Equals 对当前的 PBDOM 字符数据和 PBDOM 对象引用的等价性进行测试 GetOwnerDocumentObject 该方法返回当前 PBDOM 字符数据的 PBDOM 文档 GetName 该方法能够得到当前 PBDOM 字符数据名 GetObjectClass 该方法返回一个长整型代码来说明当前 PBDOM 对象类 GetObjectClassString 该方法返回 PBDOM 对象类的字串形式 GetParentObject 该方法返回当前 PBDOM 字符数据的 PBDOM 父类对象 GetText 调用该方法能够获得当前 PBDOM 字符数据包含的文本数据

GetTextNormalize 该方法能够得到当前 PBDOM 字符逐句对象中的文本数据,同时删除周围的白间隔字符,并缩减

内部白间隔字符为标准的单间距 GetTextTrim 该方法返回当前 PBDOM 字符数据对象的原文内容并删除了周围的白间隔字符

HasChildren 该方法若 PBDOM 字符数据至少有一个 PBDOM 对象的子类节点时,返回 true,若无子类节点,

返回 false. IsAncestorObjectOf 该方法判定当前 PBDOM 字符数据是否为其他 PBDOM 对象的祖先 SetParentObject 该方法设定引用的 PBDOM 对象为当前 PBDOM 字符数据的父类 SetText 该方法设置输入传为当前 PBDOM 字符数据对象的文本内容

第 12 章 文档对象模型

— 337 —

12.6 PBDOM_COMMENT 类

12.6.1 PBDOM_COMMENT 类定义

PBDOM_Comment 类表示为 XML 文档的 DOM 注释节点,该类可以由 PBDOM 字符

数据类继承得到,并对 PBDOM 对象类及一系列针对 DOM 注释节点进行操作的方法进行

了扩展。

在 PBDOM 中分析一个文档时,任意注释在内存中被看作是 DOM 树的结果的一部

分。PBDOM 注释类在运行期内创建,同时也成为 DOM 树的一部分。然而,一个 XML

注释并不能构成文档注释模型的一部分。

最后需要声明的一点是:有无注释与文档的有效性无关,无需在 DTD 中预先声明注释。

12.6.2 PBDOM_COMMENT 类方法

客观上说,从 PBDOM_Object 中继承的部分方法并没有实际的意义,这些方法只是

返回了默认值或默认的函数结果,这些方法见表 12-9。

表 12-9 PBDOM_Comment 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_COMMENT GetContent false GetName 字串“#comment” HasChildren false IsAncestorObjectOf false RemoveContent false IssertContent 当前的 PBDOM_COMMENT SetContent 当前的 PBDOM_COMMENT SetName false

PBDOM_COMMENT 有所列的常用重要方法,见表 12-10。

表 12-10 PBDOM_Comment 类常用方法

方 法 描 述 Append 加载的方法,包含两个语法 Clone 创建和返回当前 PBDOM 注释类的复制品 Detach 从 PBDOM 父类对象上取一个 PBDOM 注释类 Equals 对当前的 PBDOM_COMMENT 和 PBDOM_OBJECT 引用的一致性测试 GetObjectClass 返回当前 PBDOM_OBJECT 类指定的长整型代码 GetObjectClassString 返回 PBDOM_OBJECT 类的字符形式 GetOwnerDocumentObject 返回当前 PBDOM_COMMENT 的 PBDOM_DOCUMENT GetParentObject 返回当前 PBDOM_COMMENT 的 PBDOM_OBJECT 父类 GetText 能够获得当前 PBDOM_COMMENT 对象的文本数据

GetTextNormalize 能够获得当前 PBDOM_COMMENT 对象的文本数据,同时删除周围的白间隔字符,并将内部的

白间隔字符标准化为单倍距 GetTextTrim 返回当前的 PBDOM_COMMENT 对象的文本注释,并删除周围的白间隔字符 SetParentObject 设置 PBDOM 引用对象为当前的 PBDOM 注释类的父类 SetText 设置输入字串为当前的 PBDOM_COMMENT 对象的文本注释

下面这个例子采用 Clone 方法,创建了一个 XML 文档,序列化过程显示如下:

Pow erBuilder9.0 基础应用与系统开发

— 338 —

<!DOCTYPE root

[

<!ELEMENT root (level_1)*>

<!ELEMENT level_1 (level_2)*>

<!ELEMENT level_2 (#PCDATA)*>

]>

<root>

<level_1>

<!--Element at level : 1-->

<level_2>

<!--Element at level : 2-->

</level_2>

</level_1>

</root>

12.7 PBDOMDoctype 类

12.7.1 PBDOMDoctype 类定义

PBDOM_DOCTYPE 类 代 表 了 XML DOM 文 档 的 文 档 类 型 声 明 对 象 。

PBDOM_DOCTYPE 类提供了得到根元素名的方法,其中根元素和内部子集、系统以及公

共 ID 一样,隶属 DOCTYPE。

12.7.2 PBDOMDoctype 类方法

客观上说,从 PBDOM_Object 中继承的部分方法也没有实际的意义,这些方法只是

返回了默认值或默认的函数结果,这些方法在表 12-11 中列出。

表 12-11 PBDOM_Doctype 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_DOCTYPE GetContent false GetText 空串 GetTextNormalize 空串 GetTextTrim 空串 HasChildren False InsertContent 当前的 PBDOM_DOCTYPE IsAncestorObjectOf False RemoveContent false SetContent 当前的 PBDOM_DOCTYPE

PBDOM_Doctype 有所列的常用方法,见表 12-12。

表 12-12 PBDOM_Doctype 类常用方法

方 法 描 述 Clone 创建并返回当前的 PBDOM_DOCTYPE 副本

Detach 从 PBDOM 文本对象父类上取一个 PBDOM 文本类型对象。这依旧是炜进行操作前的 PBDOM 文

本类型对象的一部分,但它不再有父类 Equals 对当前的 PBDOM 文本类型和文本对象引用一致性的测试 GetInternalSubset 返回文本类型的内部子集数据

第 12 章 文档对象模型

— 339 —

(续表)

方 法 描 述 GetName 能够获得当前 PBDOM 文本对象内的根元素名 GetObjectClass 返回当前 PBDOM 对象指定类的长整型代码 GetObjectClassString 返回 PBDOM 对象类的串形式 GetOwnerDocumentObject 返回当前 PBDOM 文本类型的 PBDOM 文档 GetParentObject 返回当前 PBDOM 文本类型的 PBDOM 父类对象 GetPublicID 恢复文本类型的外部引用 DTD 声明的公共 ID SetDocument 设置当前 PBDOM 文本类型的 PBDOM 文档 SetInternalSubset 设置 PBDOM 文本类型内部子集数据 SetName 设置 PBDOM 文本类型声明的根元素名

SetParentObject 设置引用的 PBDOM 对象为当前 PBDOM 文本类型对象的父类,设定此 PBDOM 文本类型所代表

的 DOCTYPE 为所引用 PBDOM 文档的 DOCTYPE SetPublicID 设置外部引用 DTD 的公共 ID SetSystemID 设置外部引用 DTD 的系统 ID GetSystemID 恢复文档类型声明的外部引用 DTD 系统 ID

下面,举一个 Getname 方法的例子。

如果有以下 DICTYPE 声明,那么 Getname 方法返回 abc。

<!DOCTYPE abc [<!-- internal subset -->

<!ELEMENT abc (#PCDATA)> <!ELEMENT data (#PCDATA)>

<!ELEMENT inner_data (#PCDATA)>]>

12.8 PBDOMDocument 类

12.8.1 PBDOMDocument 类定义

PBDOM 文档类是对 XML DOM 文档行为的定义,能够获得根元素、指令处理以及

其他文档标准信息。该类继承于 PBDOM 对象,并对大多数 PBDOM 对象类的方法提供

了专用工具。

12.8.2 PBDOMDocument 类方法

客观上说,从 PBDOM_Object 中继承的部分方法没有实际的意义,这些方法只是返

回了默认值或默认的函数结果,这些方法见表 12-13。

表 12-13 PBDOM_Document 类特殊方法

方 法 返回值 Detach 当前的 PBDOM_DOCUMENT GetName 子串�#document� GetOwnerDocumentObject null GetParentObject null GetText 空串 GetTextNormalize 空串 GetTextTrim 空串 SetName false SetParentObject 当前的 PBDOM_DOCUMENT

PBDOM_Document 具有所列常用方法,见表 12-14。

Pow erBuilder9.0 基础应用与系统开发

— 340 —

表 12-14 PBDOM_Document 类常用方法

方 法 返回值 AddContent 能够往当前的 PBDOM 文档对象中加入一个新的 PBDOM 对象 Clone 创建当前 PBDOM 文档对象的副本 DetachRootElement 从该文档中分离根元素并返回该文档 Equals 对当前的 PBDOM 文档对象和引用的 PBDOM 对象一致性进行测试 GetContent 返回当前的 PBDOM 文档对象的所有子类内容 GetDoctype 能够恢复当前 XML DOM 文档的文档类型声明 GetObjectClass 返回指定的当前 PBDOM 对象类的长整型代码 GetObjectClassString 返回 PBDOM 对象类的串形式 GetRootElement 恢复当前 XML DOM 文档的根元素 HasChildren 如果当前的 PBDOM 文档对象至少有一个子 PBDOM 对象,返回 true,否则,返回 false HasRootElement 如果文档有根元素,返回 true InsertContent 往当前的 PBDOM 文档对象中插入一个新的 PBDOM 对象 IsAncestorObjectOf 定义当前的 PBDOM 文档对象是否是其他 PBDOM 对象的祖先 NewDocument 该方法为多语法调用的,包含语法一和二 RemoveContent 从当前的 PBDOM 文档对象中删除子对象 SaveDocument 存储 PBDOM 文档对象的 DOM 树 XML 序列化串到磁盘中 SetContent 设定 PBDOM 文本对象的全部内容,删除已存在的最早的子类 SetDoctype 设置该文档的 DOCTYPE 声明 SetRootElement 设置该文档的根元素

下面,举例说明 AddContent 方法的具体应用。

用 pbdom_elem_1、pbdom_elem_2 和 pbdom_elem_3 共 3 个元素创建 pbdom_doc1 文

档。这 3 个元素设置为 pbdom_element_1 的子类。

从 pbdom_doc1 上 拆 取 根 元 素 pbdom_doc1.GetRootElement().Detach() , 使 用

pbdom_doc1.AddContent(ref pbdom_elem_1) 语 句 , 添 加 pbdom_doc1.pbdom_elem_1 为

pbdom_doc1 的子类。

TRY

PBDOM_ELEMENT pbdom_elem_1

PBDOM_ELEMENT pbdom_elem_2

PBDOM_ELEMENT pbdom_elem_3

PBDOM_DOCUMENT pbdom_doc1

pbdom_doc1 = Create PBDOM_DOCUMENT

pbdom_elem_1 = Create PBDOM_ELEMENT

pbdom_elem_2 = Create PBDOM_ELEMENT

pbdom_elem_3 = Create PBDOM_ELEMENT

pbdom_elem_1.SetName("pbdom_elem_1")

pbdom_elem_2.SetName("pbdom_elem_2")

pbdom_elem_3.SetName("pbdom_elem_3")

pbdom_elem_1.AddContent(pbdom_elem_2)

pbdom_elem_1.AddContent(pbdom_elem_3)

pbdom_doc1.NewDocument("", "", "Root_Element", &

"", "")

pbdom_doc1.GetRootElement().Detach()

pbdom_doc1.AddContent(pbdom_elem_1)

CATCH (pbdom_exception ex)

MessageBox("Exception", ex.getMessage())

END TRY

第 12 章 文档对象模型

— 341 —

原根元素<Root_Element>已分离,并被<pbdom_elem_1>替代,文档转化如下:

<!DOCTYPE Root_Element>

<pbdom_elem_1>

<pbdom_elem_2/>

<pbdom_elem_3/>

</pbdom_elem_1

12.9 PBDOM_ELEMENT 类

12.9.1 PBDOM_ELEMENT 类定义

PBDOM 元素类定义了用 PowerScript 语言建立 XML 元素模型的行为。用户通过相应

的方法可以获得元素的文本内容、属性及其子类。

在 PBDOM 中,XML 元素的属性并非其子类。属性是元素的特性,并非从元素中分

离出来,他们之间是相关联的。元素的 PBDOM 属性对象与其他任意子类之间没有并列

关系。

12.9.2 PBDOM_ELEMENT 类方法

表 12-15 给出 PBDOM_Element 类具有的方法。

表 12-15 PBDOM_Element 类方法

方 法 返回值 AddContent 多语法调用的方法,分语法一和语法二两步 AddNamespaceDeclaration 为 PBDOM 元素对象天加一个名字间隔声明,若名字间隔为 PBDOM 元素对象的默认值,该

新名字间隔可使用 Clone 创建一个 PBDOM 元素类的副本 Detach 从 PBDOM 父类对象上取一个 PBDOM 元素对象 Equals 对通过方法调用的 PBDOM 元素对象与指定参数的 PBDOM 对象的一致性进行比较 GetAttribute 该方法为多语法调用的,具有语法一与语法二 GetAttributes 对 PBDOM 元素对象返回其 PBDOM 属性对象的全部设置 GetAttributeValue 该方法为多语法调用的,具有 4 个语法 GetChildElement 该方法为多语法调用的,具有 2 个语法 GetChildElements 该方法为多语法调用的,具有 3 个语法

GetContent 获得 PBDOM_OBJECT 对象阵列,每一个都是方法调用 PBDOM 元素对象的子节点,返回阵

列为�live�,这样阵列中任一项的改变均会影响到阵列参考的真实项 GetName 恢复 PBDOM 元素对象的本地名 GetNamespacePrefix 对 PBDOM 元素对象返回其名字间隔前缀。若无名字间隔前缀,返回空串

GetNamespaceUri 返回安置在 PBDOM 元素对象上的 URI 前缀,若无前缀,返回 PBDOM 元素对象默认的名字

间隔前缀。如没有 URI,返回空串 GetObjectClass 返回指定的当前 PBDOM 对象类的长整型代码 GetObjectClassString 返回 PBDOM 对象类的串形式 GetOwnerDocumentObject 返回包含 PBDOM 元素对象的 PBDOM 文档对象 GetParentObject 返回 PBDOM 元素对象的父类对象

GetQualifiedName 返回形式为[namespace_prefix]:[local_name]的 PBDOM 元素对象全名,若 PBDOM 元素对象

无名字间隔前缀,返回本地名

GetText 得 到 所 有 PBDOM_TEXT 的 文 本 值 和 方 法 调 用 的 PBDOM_ELEMENT 对 象 所 包 含 的

PBDOM_CDATA 节点连结值 GetTextNormalize 返回 PBDOM 元素对象的标准化的文本数据 GetTextTrim 返回 PBDOM 元素对象的文本数据,同时导致或预示白间隔字符将被删除

Pow erBuilder9.0 基础应用与系统开发

— 342 —

(续表)

方 法 返回值 HasAttributes 说明 PBDOM 元素对象是否有一个或多个属性 HasChildElements 说明 PBDOM 元素对象是否有一个或多个子类 PBDOM 元素对象 HasChildren 说明 PBDOM 元素对象是否有一个或多个子类对象 InsertContent 往 PBDOM 元素对象中插入一个新 PBDOM 对象 IsAncestorObjectOf 判定一个 PBDOM 元素对象是否是参数指定的 PBDOM 对象的祖先 IsRootElement 说明一个 PBDOM 元素对象是否是一个 PBDOM 文档对象的根元素 RemoveAttribute 多语法调用的方法,含三个语法 RemoveChildElement 多语法调用的方法,含两个语法 RemoveChildElements 多语法调用的方法,含三个语法 RemoveContent 从 PBDOM 元素对象中删除一个 PBDOM 对象,该对象的所有子类也删除

RemoveNamespaceDeclaration 对 PBDOM 元素对象删除指定的 PBDOM 名字间隔声明。若改名字间隔前缀为一空串,删除

默认的名字间隔声明 SetAttribute 多语法调用的方法,含三个语法 SetAttributes 对用当前 PBDOM 元素对象表示的 DOM 元素设置其属性

SetContent 使 用 一 个 包 含 PBDOM_OBJECT 对 象 的 阵 列 来 设 置 PBDOM 元 素 对 象 内 容 , 该

PBDOM_OBJECT 对象对于 PBHDOM 元素对象而言是合法的 SetDocument 设置 PBDOM 文档为一个 PBDOM 元素对象的父类,并设 PBDOM 元素对象为根元素 SetName 设置一个 PBDOM 元素对象的本地名,该名字实际上为元素标志名的一部分

SetNamespace 设置 PBDOM 元素对象的名字间隔。若名字间隔前缀和 URI 均提供的空串,不对 PBDOM 元

素对象分配名字间隔 SetParentObject 设置引用的 PBDOM_OBJECT 为调用的 PBDOM 元素对象父类 SetText 设置 PBDOM 元素对象的内容为给出的文本

12.10 PBDOM_ENTITYREFERENCE 对象

12.10.1 PBDOM_ENTITYREFERENCE 类定义

PBDOM_EntityReference 类定义了对一个 XML 实体引用节点的操作。可以像属性节

点一样往元素节点内插入实体引用,PBDOM 实体引用类源于 PBDOM_OBJECT。

12.10.2 PBDOM_ENTITYREFERENCE 类方法

客观上说,从 PBDOM_Object 中继承的部分方法没有实际的意义,这些方法只是返

回了默认值或默认的函数结果,这些方法见表 12-16。

表 12-16 PBDOM_EntityReference 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_ENTITYREFERENCE GetContent False GetText 空串 GetTextNormalize 空串 GetTextTrim 空串 HasChildren false InsertContent 当前的 PBDOM_ENTITYREFERENCE IsAncestorObjectOf false RemoveContent false SetContent 当前的 PBDOM_ENTITYREFERENCE

PBDOM 实体引用有所列常用方法,见表 12-19。

第 12 章 文档对象模型

— 343 —

表 12-17 PBDOM_EntityReference 类常用方法 方 法 描 述

Clone 创建并返回一个当前 PBDOM 实体引用副本 Detach 从一个父类 PBDOM_OBJECT 上去一个 PBDOM 实体引用对象 Equals 对当前 PBDOM 实体引用对象和引用 PBDOM_OBJECT 的一致性进行测试 GetName 获得当前 PBODM 实体引用对象名 GetObjectClass 返回指定的当前 PBDOM_OBJECT 类的长整型代码 GetObjectClassString 返回 PBDOM_OBJECT 类的串形式 GetOwnerDocumentObject 返回当前 PBDOM 实体引用对象的 PBDOM 文档 GetParentObject 返回当前 PBDOM 实体引用对象的 PBDOM_OBJECT 父类 SetName 改变 PBDOM 实体引用对象名,使它有效访问其他 DOM 实体对象 SetParentObject 设置引用的 PBDOM_OBJECT 为当前 PBDOM 实体引用对象的父类

12.11 PBDOM_Exceptions 类

PBDOM 定 义 了 源 于 标 准 PowerBuilder Exception 类 的 异 常 类 。 该 类 通 过

GetExceptionCode 方法扩展了异常类,该方法返回关联于正在传递的异常类的惟一代码。

表 12-18 列出了 PBDOM 异常及其代码值。

表 12-18 PBDOM 异常及代码值 异 常 值

EXCEPTION_USE_OF_UNNAMED_PBDOM_OBJECT 1 EXCEPTION_WRONG_DOCUMENT_ERROR 2 EXCEPTION_MULTIPLE_ROOT_ELEMENT 3 EXCEPTION_INAPPROPRIATE_USE_OF_PBDOM_OBJECT 4 EXCEPTION_PBDOM_OBJECT_INVALID_FOR_USE 5 EXCEPTION_PBDOM_OBJECT_ALREADY_HAS_PARENT 6 EXCEPTION_MULTIPLE_DOCTYPE 7 EXCEPTION_ILLEGAL_PBOBJECT 8 EXCEPTION_WRONG_PARENT_ERROR 9 EXCEPTION_INVALID_ARGUMENT 10 EXCEPTION_INVALID_NAME 11 EXCEPTION_DATA_CONVERSION 12 EXCEPTION_MEMORY_ALLOCATION_FAILURE 13 EXCEPTION_INTERNAL_XML_ENGINE_ERROR 14 EXCEPTION_MULTIPLE_XMLDECL 15 EXCEPTION_INVALID_STRING 16 EXCEPTION_INVALID_OPERATION 17 EXCEPTION_HIERARCHY_ERROR 18 EXCEPTION_PBDOM_OBJECT_ALREADY_HAS_OWNER 19 EXCEPTION_PBDOM_NOT_INITIALIZED 20

12.12 PBDOM_Object 类

12.12.1 PBDOM_Object 类定义

PBDOM_OBJECT 作为所有 PBDOM 类的基类,包含了每个派生类可能涉及到的所

有基本方法,每个 PBDOM_OBJECT 派生类都继承了 PBDOM_OBJECT 的基本方法,此

外还包含了他们自身特有的方法。

12.12.2 PBDOM_Object 类方法

PBDOM_OBJECT 具有所列方法,见表 12-19。

Pow erBuilder9.0 基础应用与系统开发

— 344 —

表 12-19 PBDOM_Object 类方法

方 法 描 述 AddContent 往当前的 PBDOM_OBJECT 内添加一个新的 PBDOM_OBJECT Clone 创建一个当前 PBDOM_OBJECT 的普通副本 Detach 从父类上取一个 PBDOM_OBJECT Equals 对引用的 PBDOM_OBJECT 等价性进行测试 GetContent 获得 PBDOM_OBJECT 对象阵列,阵列中的每一个子类节点均为调用的 PBDOM_OBJECT GetName 获得当前 PBDOM_OBJECT 名,返回的串取决于 PBDOM_OBJECT 所包含的 DOM 对象类型 GetObjectClass 返回指定 PBDOM_OBJECT 类的长整型代码 GetObjectClassString 返回 PBDOM_OBJECT 类的串形式 GetOwnerDocumentObject 返回当前 PBDOM_OBJECT 的 PBDOM 文档 GetParentObject 返回当前 PBDOM_OBJECT 的父类 PBDOM_OBJECT GetText 获得当前 PBDOM_OBJECT 的文本数据

GetTextNormalize 获得当前 PBDOM_OBJECT 的文本数据,并删除周围的白间隔字符,标准化内部的白间隔字

符为单倍距 GetTextTrim 获得当前 PBDOM_OBJECT 的文本数据,并删除周围的白间隔字符 HasChildren 判定 PBDOM_OBJECT 是否有子类对象 InsertContent 往当前 PBDOM_OBJECT 插入一个新的 PBDOM_OBJECT IsAncestorObjectOf 判定当前 PBDOM_OBJECT 是否是其他 PBDOM_OBJECT 的祖先 RemoveContent 从当前 PBDOM_OBJECT 中删除其子类 PBDOM_OBJECT SetContent 设定 PBDOM_OBJECT 的全部内容 SetName 设定 PBDOM_OBJECT 名 SetParentObject 设定引用的 PBDOM_OBJECT 为当前 PBDOM_OBJECT 的父类

下面给出了一个 Detach 方法的应用实例。

该例从 PBDOM_DOCUMENT 的父类对象,即 PBDOM_DOCUMENT 中取一个根元

素,并将其命名为 pbdom_doc。然后,尝试获得 PBDOM_OBJECT 父类,并用 IsValid 方

法测试其是否为空。

pbdom_obj = pbdom_doc.GetRootElement()

pbdom_obj.Detach()

pbdom_parent_obj = pbdom_obj.GetParentObject()

if (not IsValid(pbdom_parent_obj)) then

MessageBox ("Invalid", "Root Element has no

Parent")

end if

12.13 PBDOM_ProcessingInstruction 类

12.13.1 PBDOM_ProcessingInstruction 类定义

PBDOM_ProcessingInstruction 类定义了对一个 XML 操作指令的操作行为。通过其方

法可以像得到其数据一样获得操作指令对象,总能够像访问串一样访问数据,有时,和

name/value pairs 相似。

节点是操作指令对象的实际操作指令,即使指令已划分为分离的 name=“value”,对

它仍是一个串。PBDOM 支持这样的操作指令对象格式。如果操作指令对象包含对,那

么,PBDOM_ProcessingInstruction 将他们解析为内部的 name/value 对列表。

第 12 章 文档对象模型

— 345 —

12.13.2 PBDOM_ProcessingInstruction 类方法

客观上说,从 PBDOM_Object 中继承的部分方法没有实际的意义,这些方法只是返

回了默认值或默认的函数结果,这些方法见表 12-20。

表 12-20 PBDOM_ProcessingInstruction 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_ProcessingInstruction 可用 AddValue 代替 GetContent false。可用 GetName 和 GetValue 代替 HasChildren false InsertContent 当前的 PBDOM_ProcessingInstruction IsAncestorObjectOf false RemoveContent false。可用 RemoveValue 代替 SetContent 当前的 PBDOM_ProcessingInstruction 可用 SetData 代替

PBDOM_ProcessingInstruction 具有所列常用方法,见表 12-21。

表 12-21 PBDOM_ProcessingInstruction 类常用方法

方 法 描 述 Clone 创建并返回当前 PBDOM 操作指令对象副本 Detach 从当前的 PBDOM_OBJECT 上取一个 PBDOM 操作指令对象 Equals 对当前的 PBDOM 操作指令对象与提供的 PBDOM 对象一致性进行测试 GetData 返回 PBDOM 操作指令对象的原始数据 GetName 获得当前的 PBDOM 操作指令对象名

GetNames 恢复部分已被分解成 name=“value�对的 PBDOM 操作指令对象数据名列表,该方法可与

GetValue 方法连结 GetObjectClass 返回指定的当前 PBDOM 操作指令对象类的长整型代码 GetObjectClassString 返回 PBDOM 操作指令对象类的串形式 GetOwnerDocumentObject 返回当前 PBDOM 操作指令对象的 PBDOM_OBJECT GetParentObject 返回当前 PBDOM 操作指令对象的 PBDOM_OBJECT 父类 GetTarget 返回当前 PBDOM 操作指令对象的目标 GetText 获得当前 PBDOM 操作指令对象的文本数据

GetTextNormalize 获得当前 PBDOM 操作指令对象的文本数据,删除周围的白间隔字符,对内部的白间隔字符进行

标准化单倍距操作 GetTextTrim 获得当前 PBDOM 操作指令对象的文本数据,删除周围的白间隔字符 GetValue 返回当前的 PBDOM 操作指令指定的名/值对值,若无这样的对,返回空串 RemoveValue 删除指定的名/值对 SetData 设置 PBDOM 操作指令对象的原始数据 SetName 设置当前的 PBDOM 操作指令对象名 SetParentObject 设置引用的 PBDOM_OBJECT 为当前 PBDOM 操作指令对象的父类 SetValue 设置指定的名/值对的值

下面给出一个使用 Getname 方法的应用实例。

在以下操作指令中调用 Getname 方法返回 works。

<?works document="hello.doc" data="hello.wks" ?>

12.14 PBDOM_Text 类

12.14.1 PBDOM_Text 类定义

PBDOM_Text 类代表了一个 XML 文档的 DOM 文本节点,通过对指定的 DOM 文本

Pow erBuilder9.0 基础应用与系统开发

— 346 —

节点进行操作,实现 PBDOM_CHARACTERDATA 类及其方法的扩展。

PBDOM_Text 类源于 PBDOM 字符数据类,PBDOM_Text 对象常用来代表一个

PBDOM_element 或 PBDOM_Attribute 的文本内容。

PBDOM_TEXT 对象的文本包括间隔字符,例如回车、换行、tabs 及空格键等。

12.14.2 PBDOM_Text 类方法

客观上说,从 PBDOM_Object 中继承的部分方法没有实际的意义,这些方法只是返

回了默认值或默认的函数结果,这些方法见表 12-22。

表 12-22 PBDOM_Text 类特殊方法

方 法 返回值 AddContent 当前的 PBDOM_TEXT GetContent false GetName 串“#text” HasChildren false InsertContent 当前的 PBDOM_TEXT IsAncestorObjectOf false RemoveContent false SetContent 当前的 PBDOM_TEXT SetName false

PBDOM_TEXT 具有给出的重要方法,见表 12-23。

表 12-23 PBDOM_Text 类常用方法

方 法 描 述 Append 多语法调用的方法,含两个语法 Clone 创建并返回当前 PBDOM 文本对象副本 Detach 从一个父类 PBDOM_OBJECT 上取一个 PBDOM_TEXT 对象 Equals 对当前的 PBDOM_TEXT 对象和引用的 PBDOM_OBJECT 的一致性进行测试 GetObjectClass 返回当前指定的 PBDOM_OBJECT 类的长整型代码 GetObjectClassString 返回 PBDOM_OBJECT 类的串形式 GetOwnerDocumentObject 返回当前 PBDOM_TEXT 对象的 PBDOM_DOCUMENT GetParentObject 返回当前 PBDOM_TEXT 对象的父类 PBDOM_OBJECT GetText 获得当前 PBDOM_TEXT 对象的文本数据

GetTextNormalize 获得当前 PBDOM_TEXT 对象的文本数据,删除周围的白间隔字符,对内部的白间隔字符标准化

为单倍距 GetTextTrim 返回当前 PBDOM_TEXT 的文本内容,删除周围的白间隔字符 SetParentObject 设定引用的 PBDOM_OBJECT 为当前 PBDOM_TEXT 的父类 SetText 设定输入串为当前 PBDOM_TEXT 对象的文本内容

12.15 练习题

(1) 使用 PowerScript 设计一个程序,将 XML 文档载入到 PowerBuilder 环境中,并创

建一个文档对象。

(2) 设计一个程序实现在 PowerBuilder 环境中一次解析整个 XML 文档。

(3) 设计一个程序实现在 PowerBuilder 环境中验证 XML 文档。

(4) 设计一个程序实现在 PowerBuilder 环境中编辑 XML 文档。

第 13 章 人事管理系统

13.1 系统设计

13.1.1 Target(目标)设计

使用计算机实现集人员管理、工资管理和考勤管理功能于一体的人事管理系统,以代

替低效率的人工管理,并减少错误的产生。该系统适用于企业或事业单位人事部门。

13.1.2 开发设计理念

(1) 尽量采用现有的软硬件环境,及先进的管理系统开发方案,从而达到充分利用现

有资源,提高系统开发水平和应用效果的目的。

(2) 系统应符合有关人事管理的规定,满足日常工作需要,并达到操作过程中的直

观、方便、实用、安全等要求。

(3) 系统采用 C/S 结构。

(4) 系统应具备数据库维护功能,及时根据用户需求进行数据的添加、删除、修改和

备份等操作。

13.1.3 开发运行环境

以 PowerBuilder 9.0 进行前台开发,并使用 PowerBuilder 9.0 自带的 ASA 建立后台数

据库,系统可运行于 Windows 98/Me/2000/XP 等操作系统上。

13.1.4 功能分析与模块设计

系统具有考勤信息维护与统计、工资信息维护与统计、人事信息维护与统计等功能。

其功能结构如图 13-1 所示。

13.2 数据库设计

13.2.1 概念设计

系统的 E-R 图如图 13-2 所示。

员工与工资之间的对应关系为 m:n。

员工与考勤之间的对应关系为 1:m。

Pow erBuilder9.0 基础应用与系统开发

— 348 —

系统主界面

帮助 人事管理子系统 工资管理子系统 考勤管理子系统

退

图 13-1 功能结构图

员工

ID

考勤

工资

对应

对应

年龄

性别 �

ID

ID 日期 到岗时间

离岗时间 �

基本工资

交通补贴 �

月份

图 13-2 系统 E-R 结构图

第 13 章 人事管理系统

— 349 —

13.2.2 逻辑设计

根据概念结构设计结果,进行系统逻辑设计,结果如下。

员工信息(ID,姓名,年龄,性别,部门,职称,学历,籍贯,职务,参加工作时间)。

考勤信息(ID,日期,到岗时间,离岗时间,出差,加班,请假,合出勤天数)。

工资信息(ID,月份,基本工资,交通补贴,住房补贴,奖金,工会会费,公积金,

房租,水电费)。

13.2.3 物理设计

1. 建立数据库

(1) 使用 PowerBuilder 9.0 自带的 Adaptive Server Anywhere 来创建本地数据库,通过

ODBC 接口来访问后台数据库。首先用 Sybase Central 创建数据库 db_renshi,把它存在本

地机的 d:\study\工程\人事管理系统子目录下。

(2) 接 下 来 建 立 数 据 源 , 选 择 “ 开 始 ” | “ 程 序 ” |Sybase|Adaptive Server

Anywhere|ODBC Administrator,进入 ODBC 数据源管理器,添加新的数据源,命名为

ds_renshi,该数据源的数据库为 db_renshi,驱动程序为 Adaptive Server Anywhere 8.0。在

Login 标签页上,默认输入 UserID 为 dba,PassWord 为 sql。

(3) 接着再建立 DB Profile 文件,单击 PowerBuilder 9.0 工具栏中的 DB Profile 图标

,在弹出的数据库描述画板中新建一个 Profile 文件 Profile_renshi,其连接信息中的数

据源选择已经创建好的 ds_renshi,用户名默认为 dba,口令为 sql。到此为止,所要的数

据描述文件就建好了。

(4) 建立好数据库描述文件 Profile_renshi 以后,单击 PowerBuilder 9.0 的工具栏中的

数据库图标 ,进入数据库画板工作区,在欲连接的数据库描述文件上右击,在弹出菜

单中选择 Connect 子项。此时可以看到 Profile_renshi 项的底色为蓝色,其左边的图标上

有一个绿色对号,这表示已经与 db_renshi 数据库连接上了。

2. 建立数据表

(1) 根据前面的逻辑结构设计,建立三张表和一个视图。

· 员工基本信息表,命名为 t_stuffinfo,字段定义及其含义如下:

t_stuffinfo(id char(5) //员工编号

name char(10) //员工姓名

sex char(2) //性别

age integer //年龄

department char(20) //部门

title_tech_post char(15) //职称

native_place char(10) //籍贯

duty char(10) //职务

join_work_date date //参加工作时间

)

其中 id 为主键。

Pow erBuilder9.0 基础应用与系统开发

— 350 —

· 考勤信息表,命名为 t_kaoqininfo,字段定义及其含义如下:

t_kaoqininfo(id char(5) //员工编号

date date //考勤日期

arrive_time time //到岗时间

leave_time time //离岗时间

evection char(2) //出差

overtime char(2) //加班

leave char(2) //请假

fate decimal(3,1) //合出勤天数

)

其中 id 和 date 一起作为主键,id 为外键。

· 工资信息表,命名为 t_wageinfo,字段定义及其含义如下:

t_wageinfo(id char(5) //员工编号

month integer //工资发放月份

basic_wage numeric(7,2) //基本工资

traffic_subsidy numeric(5,2) //交通补贴

house_subsidy numeric(5,2) //住房补贴

prize numeric(5,2) //奖金

labour_union_tax numeric(5,2) //工会会费

accumulation_fund numeric(5,2) //公积金

rent numeric(5,2) //房租

water_elect_fee numeric(5,2) //水电费

)

其中 id 和 month 一起作为主键,id 为外键。

· 考勤信息统计视图,命名为 v_kq_stat,选取的字段如下:

v_kq_stat(t_stuffinfo.name, t_stuffinfo.department, t_kaoqininfo.id,

t_kaoqininfo.date, t_kaoqininfo.arrive_time, t_kaoqininfo.leave_time,

t_kaoqininfo.evection, t_kaoqininfo.overtime, t_kaoqininfo.leave,

t_kaoqininfo.fate)

(2) 建立表

在数据库画板中,选中欲建表的数据库对象的 Tables 节点右击弹出“添加表”菜单

项,选择该菜单项,在数据库画板工作区下面的列子窗口中按上述表结构完成数据表

t_stuffinfo 的定义。用同样的办法完成其余两个表 t_kaoqin、t_wage 的定义。

(3) 建立视图

视图的建立方法在此不再详细介绍,按照上面的视图结构建立即可。

13.3 系统其他部分

13.3.1 创建主菜单

在新建画板中,单击 PB Object 标签页,在该页中,选择 Menu,单击 OK 按钮,打

第 13 章 人事管理系统

— 351 —

开 Menu Painter。在 Menu Painter 中创建如图 13-3 所示的 MDI 菜单,命名为 m_main,每

个子菜单项按表 13-1 所示命名并锁定。并将每个子菜单项的 Text、Microhelp、Tag 属性

都设为表 13-1 中的“显示标题”列中的内容,其他属性采用默认值即可。

图 13-3 主菜单

表 13-1 M_main 菜单项

显示标题 命名(name) 人事管理子系统 M_rsglzxt

考勤管理子系统 M_kqglzxt

工资管理子系统 M_gzglzxt

帮助 M_help

关于系统 M_about

退出 M_exit

13.3.2 创建 MDI 窗口

新建一个窗体,命名为 w_main,设置窗体的 Title 属性为“主界面”,Backcolor 属

性设置为 Cream,WindowType 值设为“mdi!”,再为 w_main 的 open 事件添加如下代

码:

sqlca.dbms="ODBC"

sqlca.database="renshi"

sqlca.userid=""

sqlca.dbpass=""

sqlca.logid=""

SQLCA.DBParm = "ConnectString=’DSN=renshi;UID=dba;PWD=sql’"

connect using sqlca;

if sqlca.sqlcode<>0 then

messagebox("不能连接到数据库",sqlca.sqlerrtext)

return

end if

这段代码的功能为:当程序开始运行,打开窗口时就可实现应用程序与数据库的连

接。再为 w_main 的 close 事件添加如下代码:

disconnect; //当 MDI 窗口关闭时,就关闭数据库连接

所实现的系统主界面如图 13-4 所示。

Pow erBuilder9.0 基础应用与系统开发

— 352 —

图 13-4 系统运行主界面

13.3.3 创建父窗口

由于 3 个子系统具有部分相同的功能和界面,因此通过建立一个父窗口,实现共有的

功能,然后通过继承的方法得到子系统的窗体。

(1) 建立一个新窗体 w_father,在窗口中的适当位置放置如下控件:一个静态文本

框,Text 属性设置为“管理子系统”,BorderColor 属性设置为 Cream;一个按钮,Text 属

性设置为“退出(&Q)”;一个标签页控件,并添加一个子页,第 1 页的 TabText 属性设置

为“维护”,在该页上放置一个 Datawindow 控件,4 个按钮控件,Text 属性分别设置为

“上一条”、“下一条”、“添加”、“更新”。第 2 页的 TabText 属性设置为“统计”,由于各

子系统的“统计”页有所不同,本页先不放置控件。设置的界面如图 13-5 所示。

图 13-5 父窗口

第 13 章 人事管理系统

— 353 —

(2) 添加代码

为 w_father 窗体的 open()事件添加如下代码:

tab_1.tabpage_1.dw_1.settransobject(sqlca)

双击“上一条”按钮,为 click()事件添加如下代码:

int m

dw_1.retrieve()

m=dw_1.getrow()

m=m - 1

dw_1.scrolltorow(m)

dw_1.setfocus()

双击“下一条”按钮,为 click()事件添加如下代码:

int n

dw_1.retrieve()

n=dw_1.getrow()

n=n + 1

dw_1.scrolltorow(n)

dw_1.setfocus()

双击“添加”按钮,为 click()事件添加如下代码:

dw_1.scrolltorow(dw_1.insertrow(dw_1.getrow()+1))

双击“更新”按钮,为 click()事件添加如下代码:

dw_1.settransobject(sqlca)

dw_1.update()

commit using sqlca;

13.3.4 创建关于窗口

新建窗口 w_about,将其属性中的 Windowtype 属性改为“response!”。在窗口中加入

两个静态文本框,一个图片控件,一个“确定”按钮控件。为“确定”按钮添加如下脚本:

close(w_about) //关闭窗口 w_about

在图片控件 PictureName 属性中输入或单击该属性右边的 图标,可以选择欲插入图

片的路径和文件名。结果如图 13-6 所示。

13.4 人事管理子系统

13.4.1 创建数据窗口对象

人事管理子系统中需要创建两个数据窗口对象,分别命名为 d_stuff_maintenance,

d_stuff_stat。先创建 d_stuff_maintenance,单击主菜单 File|New,选择 DataWindow 标签

Pow erBuilder9.0 基础应用与系统开发

— 354 —

页,显示风格用 FreeForm 类型,数据源为 Quick Select,选择表 t_stuffinfo 的全部字段,

显示设置如图 13-7 所示。

图 13-6 关于窗口

图 13-7 创建人事信息管理维护数据窗口对象

再创建 d_stuff_stat,方法大致相同,只是显示风格用 Grid 类型,显示设置如图 13-8

所示。再设置查询参数,设置参数的详细方法此处不再详细介绍,设置结果如图 13-9 所

第 13 章 人事管理系统

— 355 —

示。

图 13-8 创建人事信息管理统计数据窗口对象

图 13-9 设置员工信息统计查询参数

13.4.2 创建窗体

人事管理子系统的窗体从 w_father 窗体继承而来,继承方法如下。

(1) 在系统树中选中 w_father 右击,选择 Inherit,则由父窗口 w_father 继承生成了新

Pow erBuilder9.0 基础应用与系统开发

— 356 —

窗口,保存为 w_stuff。设置 Title 为“人事管理子系统”,设置 BackColor 为 Cream,将静

态文本框的 Text 属性改为“人事管理子系统”,将“维护”标签页的 TabText 属性该为

“员工信息维护”;将“统计”标签页的 TabText 属性设为“员工信息统计”。

(2) 选中“员工信息维护”标签页上的数据窗口 dw_1,在 DataObject 属性旁单击

,选择 d_stuff_maintenance,将数据窗口对象与数据窗口联系起来,“员工信息维护”

标签页的设置如图 13-10 所示。

图 13-10 人事管理子系统

(3) 双击“退出”按钮,为 click 事件添加代码。

close(w_stuff) //单击退出按钮关闭人事管理子系统窗体

(4) 选中“员工信息统计”标签页,在上面放置一个 GroupBox 控件,将 Text 属性设

置为“统计条件选择”。在 GroupBox 控件上放置若干个 CheckBox 控件、SingleLineEdit

控件和 CommandButton 控件,再在该标签页上放置一个 DataWindow 控件,并将该控件

和 d_stuff_stat 数据窗口对象联系。具体设计结果如图 13-11 所示。

(5) 添加代码。

双击 CheckBox,分别为 cbx_1,cbx_2,cbx_3 添加代码。

为 cbx_1 添加代码如下:

if cbx_1.checked=true then

sle_1.enabled=true

else

sle_1.enabled=false

end if

第 13 章 人事管理系统

— 357 —

图 13-11 “员工信息统计”标签页

为 cbx_2 添加代码如下:

if cbx_2.checked=true then

sle_2.enabled=true

else

sle_2.enabled=false

end if

为 cbx_3 添加代码如下:

if cbx_3.checked=true then

sle_3.enabled=true

else

sle_3.enabled=false

end if

双击“统计”按钮,添加如下代码:

string id,name,depart

id=sle_1.text

name=sle_2.text

depart=sle_3.text

dw_2.settransobject(sqlca)

if sle_1.text = "" and sle_2.text = "" and sle_3.text = "" then

messagebox("统计条件错误","统计条件不能全为空!")

else

dw_2.retrieve(id,name,depart)

end if

双击“清空”按钮,添加如下代码:

sle_1.text=""

sle_2.text=""

Pow erBuilder9.0 基础应用与系统开发

— 358 —

sle_3.text=""

13.4.3 与主菜单联系

在系统树中双击 m_main,出现主菜单设计界面,双击“人事管理子系统”,为 click

事件添加代码:

open(w_stuff) //显示人事管理子系统窗口

以相同的方法为余下的修改信息菜单的 click 事件添加相应代码,用来在运行时显示

窗体。

13.5 工资管理子系统

13.5.1 创建数据窗口对象

工资管理子系统需要创建两个数据窗口对象,分别命名为 d_wage_maintenance 和

d_wage_stat。两者都选择表 t_wage 的全部字段,前者的显示风格为 FreeForm 类型,后者

的显示风格为 Grid 类型,他们的创建方法与 13.4.1 中的数据窗体的创建方法相同,这里

不再详述,只是 d_wage_stat 数据窗口对象定义的查询参数不同,如图 13-12 所示。

图 13-12 设置工资信息统计查询参数

13.5.2 创建窗体

(1) 按 13.4.2 中介绍的方法从 w_father 继承产生工资管理子系统的窗体,保存为

w_wage。

第 13 章 人事管理系统

— 359 —

(2) 将数据窗口对象 d_wage_maintenance 与“工资信息维护”标签页上的 dw_1 联

系,“工资信息维护”标签页的设置结果如 13-13 所示。

图 13-13 工资管理系统

(3) 双击“退出”按钮,为 click 事件添加代码:

close(w_wage) //单击退出按钮关闭工资管理子系统窗体

(4) 与人事管理子系统类似,在“工资信息统计”标签页上放置若干 GroupBox、

SingleLineEdit 、 CheckBox 、 CommandButton|StaticText 和 DataWindow 控 件 , 并 将 该

DataWindow 和数据窗口对象 d_wage_stat 联系,设置结果如图 13-14 所示。

图 13-14 “工资信息统计”标签页

Pow erBuilder9.0 基础应用与系统开发

— 360 —

(5) 添加代码。

为 cbx_1 的 click()事件添加如下代码:

if cbx_1.checked=true then

sle_1.enabled=true

else

sle_1.enabled=false

end if

为 cbx_2 的 click()事件添加如下代码:

if cbx_2.checked=true then

sle_2.enabled=true

else

sle_2.enabled=false

end if

双击“统计”按钮,为其 click()事件添加代码:

string id

int month,i,row

decimal get,deduct,temp

id=sle_1.text

month=integer(sle_2.text) //转换为查询参数要求的数据类型

dw_2.settransobject(sqlca)

if sle_1.text = "" and sle_2.text = "" then

messagebox("统计条件错误","统计条件不能全为空!")

else

dw_2.retrieve(id,month)

get= 0

for i=3 to 6

get=dw_2.getitemdecimal(1,i) + get //计算应得的工资

next

for i=7 to 10

deduct=dw_2.getitemdecimal(1,i) + deduct //计算应扣除的金额

next

sle_3.text=string(get)

sle_4.text=string(deduct)

temp=get - deduct //计算实际发放的金额

sle_5.text=string(temp)

end if

为“清除”按钮的 click()事件添加代码:

sle_1.text=""

sle_2.text=""

为 dw_2 的 rowfocuschanged()事件添加如下代码,目的是为了当焦点落在某条记录上

时,计算该条纪录的工资发放情况。

int row,i

第 13 章 人事管理系统

— 361 —

decimal get,deduct,temp

row=dw_2.getrow() //得到当前的行号

for i=3 to 6

get=dw_2.getitemdecimal(row,i) + get //计算应得的工资

next

for i=7 to 10

deduct=dw_2.getitemdecimal(row,i) + deduct //计算应扣除的金额

next

sle_3.text=string(get)

sle_4.text=string(deduct)

temp=get - deduct //计算实际发放的金额

sle_5.text=string(temp)

13.5.3 与主菜单联系

与主菜单联系的方法在前面已经介绍,此处不再详述。

13.6 考勤管理子系统

13.6.1 创建数据窗口对象

根 据 应 用 需 要 , 考 勤 管 理 子 系 统 共 创 建 4 个 数 据 窗 口 对 象 , 分 别 命 名 为

d_kq_maintenance 、 d_kq_stat 、 d_kq_stat_dep 及 d_kq_stat_z 。 数 据 窗 口 对 象

d_kq_maintenance 的显示方式设置为 FreeForm 类型,选择表 t_kaoqin 的全部字段。设置

d_kq_stat 的显示方式为 Grid 类型,选择表 t_kaoqin 的全部字段,并且设置 3 个查询参

数,如图 13-15 所示。设置 d_kq_stat_dep 的显示方式为 Grid 类型,选择视图 v_kq_stat 的

全部字段,设置 3 个查询参数,如图 13-16 所示。设置 d_kq_stat_z 的显示方式为 Grid 类

型,选择视图 v_kq_stat 的全部字段,设置 2 个查询参数,如图 13-17 所示。

图 13-15 数据窗口对象 d_kq_stat 的查询参数设置

Pow erBuilder9.0 基础应用与系统开发

— 362 —

图 13-16 数据窗口对象 d_kq_stat_dep 的查询参数设置

图 13-17 数据窗口对象 d_kq_stat_z 的查询参数设置

13.6.2 创建窗体

创建步骤如下。

(1) 按 13.4.2 中介绍的方法从 w_father 继承产生考勤管理子系统的窗体,保存为

w_kaoqin。

(2) 将数据窗口对象 d_kq_maintenance 与“考勤信息维护”标签页上的 dw_1 联系,

将“统计”标签页更名为“个人考勤信息统计”,并产生两个新的标签页,分别命名为

“部门考勤信息统计”、“总体考勤信息统计”。考勤信息子系统设置结果如图 13-18 所

第 13 章 人事管理系统

— 363 —

示。

图 13-18 考勤管理子系统设置

(3) 为“退出”按钮的 click 事件添加代码:

close(w_kaoqin) //单击退出按钮关闭考勤管理子系统窗体

(4) 在“个人考勤信息统计”、“部门考勤信息统计”和“总体考勤信息统计”标签页

上放置若干 SingleLineEdit、CommandButton、StaticText 和 DataWindow 控件,并将

dw_2 和数据窗口对象 d_kq_stat 联系,将 dw_3 和数据窗口对象 d_kq_stat_dep 联系,将

dw_4 和数据窗口对象 d_kq_stat_z 联系,设置结果分别如图 13-19、图 13-20 和图 13-21

所示。

图 13-19 “个人考勤信息统计”标签页设置

Pow erBuilder9.0 基础应用与系统开发

— 364 —

图 13-20 “部门考勤信息统计”标签页设置 图 13-21 “总体考勤信息统计”标签页设置

(5) 为“个人考勤信息统计”标签页添加代码。

双击“统计”按钮,添加如下代码:

string id

int i,row,late,evection,overtime,leave

decimal stat

date date_1,date_2

id=sle_1.text

date_2=date(sle_2.text)

date_1=date(sle_3.text)

dw_2.settransobject(sqlca)

if sle_1.text = "" and sle_2.text = "" and sle_3.text = "" then

messagebox("统计条件错误","统计条件不能为空!")

else

dw_2.retrieve(id,date_2,date_1) //查询

row = dw_2.rowcount() //得到查询到的记录数

late = 0

for i=1 to dw_2.rowcount() //从第 1 行记录开始进行统计

if dw_2.getitemtime(i,3)>08:00:00 then //迟到情况统计

late = late + 1

end if

if dw_2.getitemstring(i,5) = "是" then //出差次数统计

evection = evection + 1

end if

if dw_2.getitemstring(i,6) = "是" then //加班次数统计

overtime = overtime + 1

end if

if dw_2.getitemstring(i,7) = "是" then //请假次数统计

leave = leave + 1

end if

stat = stat + dw_2.getitemdecimal(i,8) //合出勤天数计算

next

sle_4.text = string(late)

sle_5.text = string(evection)

第 13 章 人事管理系统

— 365 —

sle_6.text = string(overtime)

sle_7.text = string(stat)

sle_8.text = string(leave)

end if

为“清空”按钮的 click()事件添加代码:

sle_1.text = ""

sle_2.text = ""

sle_3.text = ""

(6) 为“部门考勤信息统计”标签页添加代码。

为“统计”按钮的 click()事件添加如下代码:

int i,row,late,evection,overtime,leave

date date_1,date_2

department=sle_11.text

date_2=date(sle_9.text)

date_1=date(sle_10.text)

dw_3.settransobject(sqlca)

if sle_9.text = "" and sle_10.text = "" and sle_11.text = "" then

messagebox("统计条件错误","统计条件不能为空!")

else

dw_3.retrieve(department,date_2,date_1)

row = dw_3.rowcount()

late = 0

for i=1 to dw_3.rowcount()

if dw_3.getitemtime(i,5)>08:00:00 then

late = late + 1

end if

if dw_3.getitemstring(i,7) = "是" then

evection = evection + 1

end if

if dw_3.getitemstring(i,8) = "是" then

overtime = overtime + 1

end if

if dw_3.getitemstring(i,8) = "是" then

leave = leave + 1

end if

next

sle_12.text = string(late)

sle_13.text = string(evection)

sle_14.text = string(overtime)

sle_15.text = string(leave)

end if

为“清空”按钮的 click()事件添加如下代码:

sle_9.text = ""

sle_10.text = ""

le_11.text = ""

Pow erBuilder9.0 基础应用与系统开发

— 366 —

(7) 为“总体考勤信息统计”标签页添加代码。

为“统计”按钮的 click()事件添加如下代码:

int i,row,late,evection,overtime,leave

date date_1,date_2

date_2=date(sle_16.text)

date_1=date(sle_17.text)

dw_4.settransobject(sqlca)

if sle_16.text = "" and sle_17.text = "" then

messagebox("统计条件错误","统计条件不能为空!")

else

dw_4.retrieve(date_2,date_1)

row = dw_4.rowcount()

late = 0

for i=1 to dw_4.rowcount()

if dw_4.getitemtime(i,5)>08:00:00 then

late = late + 1

end if

if dw_4.getitemstring(i,7) = "是" then

evection = evection + 1

end if

if dw_4.getitemstring(i,8) = "是" then

overtime = overtime + 1

end if

if dw_4.getitemstring(i,8) = "是" then

leave = leave + 1

end if

next

sle_18.text = string(late)

sle_19.text = string(evection)

sle_20.text = string(overtime)

sle_21.text = string(leave)

end if

为“清空”按钮的 click()事件添加如下代码:

sle_16.text = ""

sle_17.text = ""

13.6.3 与主菜单联系

与主菜单联系的方法在前面已经介绍,此处不再详述。

13.7 小结

通过人事管理系统范例的学习,初步了解使用 PowerBuilder 9.0 和其自带的 ASA 建

立简单数据库管理系统的过程和方法,熟悉一些常用控件的使用方法,应能够依照此范例

自行创建小型数据库管理系统,进行数据的添加、修改、查询、统计等工作。

第 14 章 学生成绩管理信息系统开发实例

14.1 系统设计

14.1.1 Target(目标)设计

由于人工管理成绩方法存在着效率不高、容易产生错误、不易修改和查找、而且以前

的成绩资料容易丢失或破损等问题,对教学管理产生不良影响,因此,希望使用计算机软

件进行管理,以弥补人工管理的缺点,方便使用。

14.1.2 开发设计理念

(1) 尽量采用现有的软硬件环境,及先进的管理系统开发方案,从而达到充分利用资

源,提高系统开发水平和应用效果的目的。

(2) 系统应符合有关教学的规定,满足学生成绩管理工作的需要,并达到操作过程中

的直观、方便、实用、安全等要求。

(3) 系统采用 C/S 结构。

(4) 系统应具备数据库维护功能,根据用户需求及时进行数据的添加、删除、修改、

备份等操作。

14.1.3 开发运行环境

使用 PowerBuilder 9.0 进行软件开发,数据库使用 PowerBuilder 9.0 自带的软件创

建,本系统能够运行在 Windows 98/Me/2000/XP 上。

14.1.4 功能分析与模块设计

本系统具有如下功能:成绩输入、成绩查询、成绩统计、信息修改等。功能结构如图

14-1 所示。

14.2 数据库设计

14.2.1 概念设计

系统的基本实体联系图(E-R 图)如图 14-2 所示。

Pow erBuilder9.0 基础应用与系统开发

— 368 —

系统登录

系统主界面

信息修改 成绩输入 成绩查询

修 改 学

生信息

修 改 课

程信息

成绩统计

修 改 成

绩信息

修 改 用

户信息

按学号

查询

按班级

查询

单 个 成

绩输入

统计不

及格

人数

产生报表

统计某

班成绩

图 14-1 系统功能模块图

学生 课程

学号 姓名

年龄 班级

性别

课程

成绩 学分

课程学习

图 14-2 系统的基本 E-R 图

其中,学生与课程之间是 m:n 的联系。

14.2.2 逻辑设计

各个实体及其之间的联系的关系模式设计如下。

学生(学号,姓名,年龄,性别,班级)

课程(课程号,课程名,学分)

学习(学号,课程号,成绩)

用户(编号,姓名,密码)

第 14 章 学生成绩管理信息系统开发实例

— 369 —

14.2.3 物理设计

14.2.3.1 建立数据库

1. 创建数据库

PowerBuilder 创建数据库应用通常是通过 ODBC 或一些专用接口来访问后台数据库

的,为了方便学习,使用 PowerBuilder 自带的 Adaptive Server Anywhere 来创建本地数据

库。用 Sybase Central 创建数据库 db_stu_grade,并把它存在本机的 d:\study\工程\学生成

绩管理系统子目录下。

2. 建立数据源

选择“开始”|“程序”|Sybase|Adaptive Server Anywhere|ODBC Administrator,进入

ODBC 数据源管理器,通过单击“添加”按钮即可添加新的数据源,该数据源的数据库

为 db_stu_grade,驱动程序为 Adaptive Server Anywhere 8.0,在 Login 标签页上,默认输

入 UserID 为 dba,PassWord 为 sql。

3. 建立数据描述文件

有了数据源,在 PowerBuilder 中建立 DB Profile 文件,方法如下:单击 PowerBuilder

工具栏中的 DB Profile 图标 ,在弹出的数据库描述画板中新建一个 Profile 文件

Profile_stu_grade,其连接信息中的数据源选择已经创建好的 ds_stu_grade,用户名默认为

dba,口令为 sql。到此为止,所要的数据描述文件就建好了。

4. 库连接

建立好数据库描述文件 Profile_stu_grade 以后,单击 PowerBuilder 工具栏中的数据库

图标 ,进入数据库画板工作区,在欲连接的数据库描述文件上右击,在弹出菜单中选

择 Connect 子项。此时可以看到 Profile_stu_grade 项的底色为蓝色,其左边的图标上有一

个绿色√号,这表示已经与 db_stu_grade 库连接上了。

5. 创建数据表的结构

根据前面的逻辑结构设计以及它们之间的依赖关系,建 4 张表和一个视图,结构如下。

(1) 学生信息表,命名为 t_stuinfo,字段定义及含义如下。

t_stuinfo(sno char(6) //学生编号

sname char(20) //学生姓名

ssex char(2) //性别

sage decimal(3,0) //年龄

sclass char(40) //班级

)

其中 sno 为主键。

(2) 课程信息表,命名为 t_course,字段定义及含义如下:

t_course(cno char(6) //课程编号

Pow erBuilder9.0 基础应用与系统开发

— 370 —

cname char(30) //课程名

ccredit decimal(3,1) //学分

)

其中 cno 为主键。

(3) 成绩信息表,命名为 t_sc,字段定义及含义如下:

t_sc(sno char(6) //学生编号

cno char(6) //课程编号

grade decimal(3,0) //成绩

)

其中 sno 和 cno 为主键,同时也为外键。

(4) 用户信息表,命名为 t_user,字段定义及含义如下:

t_user(uid char(6) //用户编号

uname char(10) //用户名

upsw char(6) //密码

ugrade integer //用户等级

)

其中 uid 为主键。

(5) 课程成绩查询视图,命名为 v_sc,字段定义及含义如下:

v_sc(t_stuinfo.sno,t_stuinfo.sname,t_stuinfo.sclass,t_course.cname,t_cou

rse.ccredit,t_sc.grade)

6. 建立表

在数据库画板中,选中欲建表的数据库对象的 Tables 节点,点右键弹出“添加表”

菜单项,选择该菜单项,即可在数据库画板工作区下面的列子窗口中完成数据表 t_stuinfo

的定义。用同样的办法完成其余 3 个表 t_course、t_sc 和 t_user 的定义。

7. 建立视图

在数据库画板中,选中欲建视图的数据库对象的 Views 节点,点右键弹出“添加视

图”菜单项,选择该菜单项即可选择表“t_course”、“t_stuinfo”和“t_sc”,单击 Open 按

钮 , 出 现 选 择 字 段 的 画 板 , 选 择 “ t_course.cname ”、“ t_course.ccredit ”、

“t_stuinfo.sname”、“t_stuinfo.sno”、“t_stuinfo.class”和“t_sc.grade”字段作为视图要显

示的字段。如图 14-3 所示。

14.2.3.2 创建登录窗口、MDI 窗口、关于窗口、父窗口和主菜单

1 .创建主菜单

单击 File|New 菜单或单击工具栏上的 New 图标,便进入新建画板,单击 PB Object

标签页,在该页中,选择 Menu,单击 OK 按钮,打开 Menu Painter。在 Menu Painter 中

创建如图 14-4 所示的 MDI 菜单,命名为 m_main,每个子菜单项按表 14-1 所示命名并锁

定。并将每个子菜单项的 Text、Microhelp、Tag 属性都设为表 14-1 中的显示标题列中的

第 14 章 学生成绩管理信息系统开发实例

— 371 —

内容,其他属性采用默认值即可。

图 14-3 视图创建画板

图 14-4 系统菜单

表 14-1 主菜单的菜单项

显示标题 命 名 信息修改 M_xxxg

修改学生信息 M_xgxsxx

修改课程信息 M_xgkcxx

修改成绩信息 M_xgcjxx

修改用户信息 M_xgyhxx

成绩输入 M_cjsr

成绩查询 M_cjcx

按学号查询 M_axhcx

按班级查询 M_abjcx

成绩统计 M_cjtj

统计不及个人数 M_tjbjgrs

统计某班成绩 M_tjmbcj

帮助 M_help

关于 M_about

退出 M_exit

Pow erBuilder9.0 基础应用与系统开发

— 372 —

2. 创建登录窗口

(1) 创建窗口 w_main

新建一个窗口 w_main:设置 Title 属性为“登录窗口”,BackColor 设为 DeskTop。在

窗口内放置 3 个 Static Text 控件,其 Text 属性分别为“欢迎使用学生成绩管理系统”、

“用户编号”、“密码”,BorderColor 属性都设置为 DeskTop。放置两个按钮控件,其中对

应密码 Static Text 控件的 PassWord 属性被选中。设置结果如图 14-5 所示。

图 14-5 系统登录窗口

(2) 为 w_main 添加代码

现在,要在窗口 w_main 的 open 事件中添加连接数据库的脚本,这样当程序开始运

行,打开窗口 w_main 时就可实现应用程序与数据库的连接,具体脚本如下:

sqlca.dbms="ODBC"

sqlca.database="db_stu_grade"

sqlca.userid=""

sqlca.dbpass=""

sqlca.logid=""

SQLCA.DBParm = "ConnectString=’DSN=ds_stu_grade;UID=dba;PWD=sql’"

connect using sqlca;

if sqlca.sqlcode<>0 then

messagebox("不能连接到数据库",sqlca.sqlerrtext)

return

end if

dw_1.settransobject(sqlca)

(3) 为按钮控件添加代码

双击“登录”按钮,为 click 事件添加下列代码:

string uid

第 14 章 学生成绩管理信息系统开发实例

— 373 —

string psw

uid=sle_1.text

psw=sle_psw.text

if len(uid)=0 then

messagebox("输入错误","用户编号不能为空!")

return

end if

if len(psw)=0 then

messagebox("输入错误","密码不能为空!")

return

end if

dw_1.retrieve(uid,psw)

if dw_1.rowcount()<>0 then //判断是否查到相应记录

open(w_mdiframe)

if dw_1.getitemdecimal(1,"ugrade")<>1 then //判断用户是否拥有修改权限

m_main.item[1].enabled=false //无修改权限,则将修改项设置为不可用

end if

close(w_main)

else

messagebox("错误","您输入的用户编号与密码不符,请确认后重新输入!")

sle_1.text=’’

sle_psw.text=’’

return

end if

这段代码完成的功能是,当高级用户登录后,主菜单的修改项可以使用,若是普通用

户登录,主菜单的修改项变为灰色,不可使用,即普通用户没有权限进行修改操作。

双击“退出”按钮,添加如下代码:

close(w_main) //关闭登录窗口,退出应用程序

disconnect; //断开与数据库的连接

3. 为 Application Object(应用对象)添加代码

打开 Application Object(应用对象)app_client,在应用对象画板的 app_client 对象的

open 事件中添加脚本:

open(w_mdiframe) //当应用程序一运行,就打开主窗口

4. 创建 MDI 窗口

新建一个窗口 w_mdiframe:设置 Title 属性为“主界面”;BackColor 属性设置为

DeskTop。将主菜单与 w_mdiframe 联系,并将 WindowType 值设为“mdi!”,再为

w_mdiframe 的 close 事件添加如下代码:

disconnect; //当 MDI 窗口关闭时,就关闭数据库连接

系统运行后的主界面如图 14-6 所示。

Pow erBuilder9.0 基础应用与系统开发

— 374 —

图 14-6 运行时主界面

5. 创建关于窗口

关于窗口 w_about 的创建方法与创建一个普通窗口一样,直接新建即可。只是需要将

其属性中的 Windowtype 属性改为“response!”。在窗口中加入两个静态文本框,一个图

片控件,一个确定按钮控件。为确定按钮控件添加如下脚本

close(w_about) //关闭窗口 w_about

在图片控件 PictureName 属性中输入或单击该属性右边的 图标,可以选择欲插入图

片的路径和文件名。结果如图 14-7 所示。

图 14-7 关于窗口

6. 创建父窗口

利用 PowerBuilder 窗口的继承性可以方便的创建具有相似功能的窗口,本例中建立

修改与查询的父窗口,并继承建立修改与查询的子窗口,方法如下。

第 14 章 学生成绩管理信息系统开发实例

— 375 —

(1) 创建修改父窗口 w_fatherupdate。

创建一个新窗口,在窗口放置如下控件:一个 Datawindow;5 个 CommandButton,

text 属性分别输入“上一条”、“下一条”、“添加”、“更新”、“退出”,如图 14-8 所示。

图 14-8 修改父窗口 w_fatherupdate

双击“上一条”按钮,为 click 事件添加如下代码:

int m

m=dw_1.getrow()

m=m -1

dw_1.scrolltorow(m)

dw_1.setfocus()

双击“下一条”按钮,添加如下代码:

int n

n=dw_1.getrow()

n=n +1

dw_1.scrolltorow(n)

dw_1.setfocus()

双击“添加”按钮,添加如下代码:

dw_1.scrolltorow(dw_1.insertrow(dw_1.getrow()+1))

双击“更新”按钮,添加如下代码:

dw_1.update()

commit using sqlca;

“退出”按钮先不添加 click 事件,将在子窗口中分别添加。

为父窗口 w_fatherupdate 的 Open 事件添加如下代码:

dw_1.settransobject(sqlca)

(2) 创建查询父窗口 w_fatherquery。

Pow erBuilder9.0 基础应用与系统开发

— 376 —

创建一个新窗口,在窗口放置如下控件:一个 Groupbox,一个 DataWindow,一个

Statictext,一个 SingleLineEdit,两个 CommandButton,只用将各控件的 Title 属性设为如

图 14-9 所示,其他属性默认即可。

图 14-9 查询父窗口 w_fatherquery

为父窗口 w_fatherquery 的 Open 事件添加如下代码:

dw_1.settransobject(sqlca)

14.3 信息修改子系统

14.3.1 创建数据窗口对象

首先需要创建数据窗口对象,根据需要修改子系统,共需建立 4 个数据窗口对象,分

别 是 : d_stuinfoupdate , d_courseupdate , d_scupdate , d_userinfoupdate , 此 处 以 建 立

d_stuinfoupdate 为例,其他数据窗口的创建方法相同。

选择主菜单 file/new,选择 DataWindow 标签页,显示风格用 FreeForm 类型,数据源

为 Quick Select,选择表 t_stuinfo 的全部字段,显示设置如图 14-10 所示。

14.3.2 创建信息修改子窗口

共需建立 4 个数据窗口,分别是:w_stuinfoupdate,w_courseupdate,w_scupdate,

w_userinfoupdate,由于各个信息修改子窗口都是从修改父窗口 w_fatherupdate 继承下来

的,因此它们都具有相似的结构,此处以创建“修改学生信息窗口为例”,步骤如下。

(1) 在系统树中选中查询父窗口右击,选择 Inherit,则由查询父窗口 w_fatherupdate

继承生成了新窗口,保存为 w_stuinfoupdate,Title 设置为“修改学生信息”,设置

BackColor 为 Desktop。

第 14 章 学生成绩管理信息系统开发实例

— 377 —

图 14-10 FreeForm 型显示风格

(2) 选中数据窗口 dw_1,在 DataObject 属性旁单击 ,选择 d_stuinfoupdate,就将

数据窗口对象与数据窗口联系起来。

(3) 双击“退出”按钮,为 click 事件添加代码:

close(w_stuinfoupdate) //单击退出按钮关闭修改学生信息窗口

窗口设置如图 14-11 所示。

图 14-11 信息修改窗体 w_stuinfoupdate

14.3.3 与主菜单联系

在系统树中双击 m_main,出现主菜单设计界面,双击“修改学生信息”,为 click 事

件添加代码:

open(w_stuinfoupdate) //显示修改学生信息窗口

Pow erBuilder9.0 基础应用与系统开发

— 378 —

以相同的方法为余下的修改信息菜单的 click 事件添加相应代码,用来在运行时显示

窗体。

上述数据窗口与窗口的对应关系见表 14-2。

表 14-2 数据窗口与窗口的对应关系

数据窗口 窗 口 D_stuinfoupdate W_stuinfoupdate D_courseupdate W_courseupdate D_scupdate W_scupdate D_userinfoupdate W_userinfoupdate

14.4 成绩录入子系统

1. 创建数据窗口对象

成绩录入子系统的数据窗口对象可以使用数据窗口对象 d_scupdate,因为数据窗口对

象 d_scupdate 可以进行输入操作,在数据窗口中只要屏蔽掉更新的操作即可,具体方法见

下面的创建数据窗口部分。

2. 创建数据窗口

接下来建立窗体,成绩录入子系统的窗体 w_singlegradein 与信息修改窗体有相似之

处,因此从 w_fatherupdate 继承产生新的窗体,方法在 14.3 中已经介绍过,这里不再详细

介绍。在 w_singlegradein 窗体进行如下属性设置:“上一个”、“下一个”按钮的 visible 属

性为 false,“更新”按钮改名为“提交”。数据窗口 dw_1 的 DataObject 属性设为

d_scupdate,使数据窗口与数据窗口控件联系。窗体的 BackColor 属性设置为 DeskTop,

其余属性均用默认值,设置完的窗体界面如图 14-12 所示。

图 14-12 成绩录入窗体 w_singlegradein

为成绩输入窗体的 open 事件添加如下代码:

dw_1.scrolltorow(dw_1.rowcount()) //指向记录的最后一条,方便再最后添加

cb_4.enabled=false //避免没有添加新记录就提交

dw_1.visible=false //避免对最后一条记录修改

第 14 章 学生成绩管理信息系统开发实例

— 379 —

3. 与主菜单联系

按照 14.3 中介绍的方法,将菜单与成绩输入窗体联系。

14.5 成绩查询子系统

14.5.1 创建数据窗口对象

根据需要,建立两个数据窗口对象,分别是:d_scsnoquery 和 d_scclassquery,这里

以创建 d_scsnoquery 为例。

单击主菜单 file/new,选择 DataWindow 标签页,显示风格用 Grid 类型,数据源为

Quick Select,选择视图 v_sc 的全部字段并设置标题,创建好的数据窗口对象如图 14-13

所示。

图 14-13 数据窗口对象 d_scsnoquery

单击工具栏上的 按钮,修改 SQL 选择语法,单击工具栏上的 Design/Retrieval

Arguments�,设置变量“sno”,数据类型为 string,单击 OK 按钮确定。选择 where 标签

页,设置选择条件,注意 value 值一定要在变量前加“:”,如图 14-14 所示。设置完毕后

单击工具栏上的 ,即可回到数据窗口对象的设计界面。 数据窗口 d_scclassquery 与 d_scsnoquery 类似,只需在设置变量时将“sno”改为

“class”、value 值“:sno”改为“:class”即可。

14.5.2 创建数据窗口

与信息修改子系统类似,查询窗口 w_snoquery 和 w_classquery 是从 w_fatherquery 继

承产生的,其创建方法相同,这里不再叙述。不同的地方是,为查询窗口 w_snoquery 的

“查询”按钮的 click 事件添加如下代码:

string sno

sno=trim(sle_1.text)

Pow erBuilder9.0 基础应用与系统开发

— 380 —

if len(sno)=0 then

messagebox("输入错误","学号不能为空,请重新输入!")

return

end if

dw_1.retrieve(sno) //指定参数进行检索

图 14-14 数据窗口参数设置

为“退出”按钮的 click 事件添加如下代码:

close(w_snoquery)

为窗口 w_classquery 的“查询”按钮的 click 事件添加如下代码:

string class

class=trim(sle_1.text)

if len(class)=0 then

messagebox("输入错误","学号不能为空,请重新输入!")

return

end if

dw_1.retrieve(class) //指定参数进行检索

为“退出”按钮的 click 事件添加如下代码:

close(w_classquery)

14.5.3 与主菜单联系

与主菜单的联系方法在 14.3 中已经介绍,此处不再详述。

14.6 成绩统计及打印子系统

14.6.1 创建数据窗口对象

创建数据窗口对象的方法前面已经介绍,这里不再详述,创建的数据窗口对象命名为

第 14 章 学生成绩管理信息系统开发实例

— 381 —

d_statfail,显示风格用 Grid 类型,数据源为 Quick Select,选择视图 v_sc 的全部字段并设

置列名,创建好的数据窗口对象如图 14-15 所示。

图 14-15 数据窗口对象 d_statfail

修改 SQL 选择语法,创建两个变量“class”和“cname”,Type 都为 String 类型,方

法同 14.5,如图 14-16 所示。

图 14-16 定义变量“class”和“cname”

14.6.2 创建窗体

首先,按前面提到的方法创建一个新的窗体,保存为 w_statfail。在窗体中放置如下

Pow erBuilder9.0 基础应用与系统开发

— 382 —

控件:先建一个 GroupBox 控件,Text 属性命名为“统计方式”,Font 标签页内的

BackColor 属性设置为 Desktop。在 GroupBox 控件内放置两个 RadioButton 控件,Text 属

性分别命名为“按班级统计”和“按课程统计”,在 Font 标签页内的 BackColor 属性设置

为 Desktop。再在 GroupBox 控件内放置两个 CommandButton 控件,Text 属性分别设置为

“统计”和“退出”;在 GroupBox 控件外放置一个 StaticText 控件,Text 属性设置为“不

及格人数:”,在 Font 标签页内的 BackColor 属性设置为 Desktop。在 StaticText 控件旁放

置一个 SingleLineEdit 控件,用于显示统计结果,属性使用默认值即可。最后放置一个

DataWindow 控件,其 DataObject 属性设置为 d_statfail。设置完成后,将 dw_1 的 Visible

属性设置为 False,因为不需要在 dw_1 中显示查询结果,只需按要求统计数量即可。其

版面布置如图 14-17 所示。

图 14-17 统计窗体 w_statfail

14.6.3 添加代码

(1) 双击“按班级统计”RadioButton 控件,为其 click 事件添加如下代码:

if rb_1.checked=true then

sle_1.enabled=true

sle_2.enabled=false

sle_2.text=""

end if

(2) 双击“按课程统计”RadioButton 控件,为其 click 事件添加如下代码:

if rb_2.checked=true then

sle_2.enabled=true

sle_1.text=""

sle_1.enabled=false

end if

(3) 双击“退出”CommandButton 控件,为其 click 事件添加如下代码:

close(w_statfail)

第 14 章 学生成绩管理信息系统开发实例

— 383 —

(4) 为窗口 w_statfail 的 open 事件添加如下代码:

dw_1.settransobject(sqlca)

(5) 双击“统计”CommandButton 控件,为其 click 事件添加如下代码:

int count

int i

int get

string class

string cname

sle_3.text=’’ //清除上次统计结果

class=trim(sle_1.text)

cname=trim(sle_2.text)

if rb_1.checked=true then

if len(class)=0 then

messagebox("输入错误","班级不能为空,请重新输入!")

return

end if

elseif rb_2.checked=true then

if len(cname)=0 then

messagebox("输入错误","课程不能为空,请重新输入!")

return

end if

end if

dw_1.retrieve(class,cname) //根据参数检索

i=dw_1.getrow() //得到第 1 行的行数

count=0 //计数变量清零

if i<>dw_1.rowcount() then //若没到最后一行

get=dw_1.getitemdecimal(i,"grade") //取数据窗口的第 i 行的 grade 列的值

if get<60 then //比较是否及格

count=count+1 //不及格,则计数变量加一

else

dw_1.scrollnextrow() //及格,判断下一条记录

end if

end if

sle_3.text=string(count) //转换数据格式,输出

14.6.4 与主菜单联系

与主菜单的联系方法在 14.3 中已经介绍,此处不再详述。

14.6.5 统计某班的成绩模块

本模块与按班级统计成绩相同,只是在统计出该班的成绩之后制作成报表,方便打

印,此处不再详述。

Pow erBuilder9.0 基础应用与系统开发

— 384 —

14.7 小结

通过本例的学习,可以了解使用 PowerBuilder 9.0 建立一个简单的数据库应用系统的

方法,本例中介绍了数据窗口对象、菜单、数据窗口的建立方法,以及使用继承的方法产

生数据窗口(继承的方法可以方便地产生多个具有类似界面和功能的窗体),同时,本章还

介 绍 了 CommandButton , GroupBox , RadioButton , StaticText , SingleLineEdit ,

DataWindow 等控件,这些控件可以方便地完成用户与数据库的数据交换,完成交互的功

能,并且,本系统简单地实现了分权限管理,高级用户可以修改信息,普通用户不可修改

信息。

第 15 章 网上采购管理信息系统开发实例

网上采购管理信息系统是一个基于 Internet 的服务系统。它使企业改变了通常用人工

进行采购的处理方式,取而代之的是高效的、规范化的解决方案。这一方案使企业的进、

销、存整个供应链的成本降低,效率提高。

网上采购有以下优点。

(1) 使企业快捷高效地处理申请订单、采购清单、询价请求及各种记账单据,从而缩

短采购周期,提高采购效率,并能有效地管理整个采购过程。

(2) 降低采购成本,节省大量资金。如果采购员能够从常规的事务处理中解脱出来,

将有更多的精力去发展客户,进行企业购销的成本分析,并且能更有效地把握节省开支的

机会。采购人员可全面掌握所采购物品的价格、质量等有关信息,在此基础上与供应商进

行价格谈判,因而可以有效地节省资金。此外,所有采购管理信息全部通过采购系统在线

完成,可以节省通信费用及纸张消耗。

15.1 系统设计

15.1.1 目标设计

网上采购系统用于完成企业从订单申请、订单平衡汇总、采购报价、比质比价到签订

合同、财务付款等一系列的业务活动。网上采购系统以缩短采购周期、提高采购效率、降

低采购成本为目标。整个采购过程的输入、查询、跟踪和确认等工作全部由计算机完成。

15.1.2 开发设计理念

(1) 尽量采用企业现有的软硬件环境,及先进的管理系统开发方案,从而达到充分利

用企业现有资源,提高系统开发水平和应用效果的目的。

(2) 系统应符合生产、采购的规定,满足企业日常工作需要,并达到操作过程中的直

观、方便、实用、安全等要求。

(3) 系统采用先进的 B/S 体系结构和传统的 C/S 结构相结合,计划申报和供应商报价

模块采用 B/S 结构,其余的模块采用 C/S 结构。

(4) 系统采用模块化程序设计方法,既便于系统功能的各种组合和修改,又便于未参

与开发的技术人员补充、维护。

(5) 系统应具备数据库维护功能,及时根据用户需求进行数据的添加、删除、修改、

备份等操作。

15.1.3 开发运行环境

后台采用 Oracle 9i 数据库管理系统,前端采用 PowerBuilder 9.0 作为应用开发工具,

Pow erBuilder9.0 基础应用与系统开发

— 386 —

应用服务器为 EAServer5.0。客户端软件在 Windows98/ME/2000 或 WindowsXP 下均可安

装使用,客户端浏览器采用 IE5.0 及以上版本。

15.1.4 功能分析与模块设计

系统包括订单管理、合同审核管理、网上付款管理等模块。系统功能模块结构如图

15-1 所示。

网上采购管理系统

������

������

���

� ����

������

���

����

图 15-1 系统功能模块图

本章主要介绍除系统管理、报表和帮助以外的其他 4 个模块。

15.2 数据库设计

15.2.1 需求分析

采购管理系统的数据流程如图 15-2 所示。

网上申请

平衡汇总

供应商

生产单位 订单

平衡表

报价 合同 比质比价

付款

图 15-2 数据流程图

第 15 章 网上采购管理信息系统开发实例

— 387 —

通过对企业采购管理的内容和数据流程分析,结合本例实际,数据项和数据结构设计

如下。

供应商信息:供货单位编号、名称、信誉度、税号、地址信息。

人员信息:人员编号、用户名、人员姓名、密码。

物资信息:物资编码、物资名称、单位、规格。

采购计划信息:订单号、采购物资信息、申请人、申请日期、需求数量、需求日期、

物资信息。

平衡表信息:平衡编号、计划号、采购物资信息、平衡人、平衡日期。

报价单信息:物资信息、报价单位、报价信息、报价日期。

合同信息:合同编号、采购物资信息、甲方信息、乙方信息、签订日期。

审批表信息:审批编号、合同编号、审批意见、审批日期。

付款通知信息:付款通知单号、乙方单位信息、承办人、录入日期。

有了上面的数据结构、数据项和数据流程,就可以进行数据库设计。

15.2.2 概念设计

根据上述分析,设计规划出的实体有:供应商、人员、物资、订单、平衡、报价单、

合同、审批表、付款通知单。各个实体的 E-R 图及其关系描述如下。

图 15-3 为供应商实体 E-R 图。

供应商

供应商编号、名称 地址信息 信誉度 税号

图 15-3 供应商实体 E-R 图

图 15-4 为采购计划实体 E-R 图。

采购计划

计划号 采购物资信息 申请日期 申请人

图 15-4 采购计划实体 E-R 图

图 15-5 为报价单实体 E-R 图。

Pow erBuilder9.0 基础应用与系统开发

— 388 —

报价单

物资信息 报价日期 报价单位 报价信息 平衡号

图 15-5 报价单实体 E-R 图

图 15-6 为平衡表实体 E-R 图。

平衡表

平衡编号 平衡人 采购物资信息 计划号 平衡日期信息

图 15-6 平衡表实体 E-R 图

图 15-7 为合同实体 E-R 图。

合同

合同编号 乙方信息 采购物资信息 甲方信息 签订日期信息

图 15-7 合同实体 E-R 图

图 15-8 为付款通知实体 E-R 图。

付款通知

付款通知单号 录入日期 乙方单位信息 承办人

图 15-8 付款通知实体 E-R 图

图 15-9 为物资信息实体 E-R 图。

第 15 章 网上采购管理信息系统开发实例

— 389 —

物资信息

物资编码 规格 物资名称 计量单位

图 15-9 物资信息实体 E-R 图

图 15-10 为审批表实体 E-R 图。

审批表

审批编号 审批日期 合同编号 审批意见

图 15-10 审批表实体 E-R 图

图 15-11 为人员实体 E-R 图。

人员

人员编号 密码 人员姓名 用户名

图 15-11 人员实体 E-R 图

实体和实体之间的联系 E-R 图如图 15-12 所示。

人员 采购计划 申请 平衡表 汇总

合同 供应商 报价单 录入 产生

审批表 付款通知单 合同 审批 通过

图 15-12 实体关系 E-R 图

Pow erBuilder9.0 基础应用与系统开发

— 390 —

15.2.3 逻辑与物理设计

数据库设计分逻辑设计和物理设计两个阶段。在逻辑数据库设计阶段,要标识数据库

中描述的重要对象以及这些对象之间的关系。在物理数据库设计阶段,要确定逻辑设计如

何在目标 DBMS 中物理地实现。

在上述实体以及实体之间联系的基础上,形成数据库中的表以及各个表之间的联系。

网上采购管理系统数据库中各个表的设计结果如下面的几个表格所示。每个表格表示数据

库中的一个表。

表 15-1 为人员信息表 renyuan。

表 15-1 人员信息表 renyuan

列 名 数据类型 可否为空 说 明 ry_rybh VARCHAR2(10) NOT NULL 人员编号(主键) ry_ryxm VARCHAR2(12) NULL 人员姓名 ry_yhm VARCHAR2(10) NOT NULL 用户名 ry_mm VARCHAR2(10) NOT NULL 密码(默认为 111111) ry_lx VARCHAR2(2) NOT NULL 类型(2 为供应商

3 为企业报价人员)

表 15-2 为供应商信息表 gongyingshang。

表 15-2 供应商信息表 gongyingshang

列 名 数据类型 可否为空 说 明 gys_gysbh VARCHAR2(20) NOT NULL 供应商编号(主键) gys_gysmc VARCHAR2(100) NOT NULL 供应商名称 gys_xyd VARCHAR2(2) NULL 信誉度 gys_fzr VARCHAR2(12) NULL 负责人 gys_sh VARCHAR2(20) NULL 税号 gys_dz VARCHAR2(100) NULL 地址

表 15-3 为采购计划表 jihua。

表 15-3 采购计划表 jihua

列 名 数据类型 可否为空 说 明 jh_jhbh VARCHAR2(20) NOT NULL 计划编号(主键) jh_sqr VARCHAR2(10) NOT NULL 申请人 jh_sqrq DATE NULL 申请日期 jh_cbr VARCHAR2(10) NULL 承办人 jh_phbh VARCHAR2(20) NULL 平衡编号 jh_htbh VARCHAR2(20) NULL 合同编号 jh_cgwzbm VARCHAR2(20) NULL 采购物资编码 cg_xqsl NUMBER(20,5) NULL 需求数量 cg_xqrq DATE NULL 需求日期 cg_zlyq VARCHAR2(20) NULL 质量要求(主键)

表 15-4 为计划平衡表 pingheng。

第 15 章 网上采购管理信息系统开发实例

— 391 —

表 15-4 计划平衡表 pingheng

列 名 数据类型 可否为空 说 明 ph_phbh VARCHAR2(20) NOT NULL 平衡编号(主键) ph_cbr VARCHAR2(10) NULL 承办人 ph_phrq DATE NULL 平衡日期 ph_xqrq DATE NULL 需求日期 ph_cgwzbm VARCHAR2(20) NULL 采购物资编码 ph_kcsl NUMBER(20,5) NULL 库存数量 ph_jhcgsl NUMBER(20,5) NULL 计划采购数量 ph_sjcgsl NUMBER(20,5) NULL 实际采购数量

表 15-5 为报价信息表 baojia。

表 15-5 报价信息表 baojia

列 名 数据类型 可否为空 说 明 bj_xh VARCHAR2(10) NOT NULL 序号(主键) bj_gysbh VARCHAR2(10) NULL 供应商编号 bj_bjrq DATE NULL 报价日期 bj_phbh VARCHAR2(10) NULL 平衡编号 bj_cgwz VARCHAR2(20) NULL 采购物资编码 bj_bjdj NUMBER(20,5) NULL 报价单价 bj_cgsl NUMBER(20,5) NULL 采购数量 bj_bjzj NUMBER(20,5) NULL 报价总计 bj_dhrq DATE NULL 到货日期

表 15-6 为合同表 hetong。

表 15-6 合同表 hetong

列 名 数据类型 可否为空 说 明 ht_htbh VARCHAR2(10) NOT NULL 合同编号(主键) ht_qdrq DATE NULL 签订日期 ht_dhrq DATE NULL 到货日期 ht_jfdwmc VARCHAR2(50) NULL 甲方单位名称 ht_jfqdr VARCHAR2(10) NULL 甲方签订人 ht_yfdwmc VARCHAR2(50) NULL 乙方单位名称 ht_yfqdr VARCHAR2(10) NULL 乙方签订人 ht_cgwzbm VARCHAR2(20) NULL 采购物资编码 ht_hkzj NUMBER(20,5) NULL 货款总计

ht_bz VARCHAR2(10) NULL 标志(0,未审批;1,顺序 1 已审批;2,顺序 2 已审

批;3,顺序 3 已审批。审批完成)

表 15-7 为审批表 shenpi。

表 15-7 审批表 shenpi

列 名 数据类型 可否为空 说 明 sp_xh VARCHAR2(10) NOT NULL 序号(主键) sp_htbh VARCHAR2(20) NOT NULL 合同编号 sp_sprq DATE NULL 审批日期 sp_spr VARCHAR2(10) NULL 审批人 sp_spyj VARCHAR2(2) NULL 审批意见 sp_spsx VARCHAR2(2) NULL 审批顺序 sp_bzsm VARCHAR2(100) NULL 备注说明

Pow erBuilder9.0 基础应用与系统开发

— 392 —

表 15-8 为付款通知单表 fukuan。

表 15-8 付款通知单表 fukuan

列 名 数据类型 可否为空 说 明 fk_fktzbh VARCHAR2(10) NOT NULL 序号(主键) fk_htbh VARCHAR2(20) NULL 合同编号 fk_yjdwmc VARCHAR2(20) NULL 乙方单位名称 fk_sh VARCHAR2(20) NULL 税号 fk_fk_lrrq DATE NULL 审批日期 fk_cbr VARCHAR2(20) NULL 承办人

表 15-9 为物资编码表 wuzi。

表 15-9 物资编码表 wuzi

列 名 数据类型 可否为空 说 明 wz_wzbm VARCHAR2(20) NOT NULL 物资编码(主键) wz_wzmc VARCHAR2(20) NULL 物资名称(主键) wz_jldw VARCHAR2(10) NULL 计量单位(主键) wz_gg VARCHAR2(20) NULL 规格(主键) wz_wzlx VARCHAR2(10) NULL 物资类型(主键) wz_kcsl NUMBER(20,4) NULL 库存数量

15.3 系统主窗口

网上采购管理系统有基于 B/S 的模块和基于 C/S 的模块。其中计划管理子系统下的计

划申报模块和报价比价子系统下的供应商报价模块,用 B/S 方式实现;其余的都在 C/S 下

实现。为此,系统需要创建两个应用程序对象:Application 和 JSP Target。

15.3.1 C/S 下主窗口

网上采购管理系统应用程序对象的创建步骤如下。

(1) 单击菜单 File|New,选择 Target 标签下的 Application Painter(应用画板),单击 OK

按钮,设置对象名为 cs_cggl,库文件名为 cs_cggl.pbl,单击 finish 按钮。

在应用程序对象 cs_cggl 的 Open 事件中输入如下的程序代码:

// Profile wscg 使用 ODBC 连接

//设置连接数据库的参数

SQLCA.DBMS = "ODBC"

SQLCA.AutoCommit = False

SQLCA.DBParm = "ConnectString=’DSN=wscg;UID=wscggl;PWD=123456’"

//连接

connect using SQLCA;

if sqlca.sqlcode<>0 then

messagebox("Cannot Connect to Database",sqlca.sqlerrtext)

return

end if

//打开登录窗口

open(w_welcome);

第 15 章 网上采购管理信息系统开发实例

— 393 —

在应用程序对象的变量定义窗口中定义系统的全局变量,代码如下:

String yhm; //用户名

String ryxm; //用户姓名

string rylx; //人员类型

String htbh; //合同编号

(2) 单击菜单 File|New,选择 Target 标签下的 JSP Target 画板,单击 OK 按钮,按照

向导一步步完成 JSP Target 对象的实现。其中,JSP Target 对象名为 wscggl,JSP Server 为

EAServer。

(3) 设计 C/S 方式下的登录窗口。单击菜单 File|New,选择 PBObject 标签下的

Window 画板,新建一个窗口。登录窗口如图 15-13 所示。窗口中放置了 3 个 Static Text

控件,2 个 Single line Edit 控件,一个供用户输入用户名(sle_1),另一个供用户输入口

令,还有 2 个按钮控件。各个控件的属性设置见表 15-10。

图 15-13 登录窗口

表 15-10 登录窗口中各个控件的属性设置表

控 件 属 性 属性值 Text 欢迎进入采购管理系统 st_1 TextSize 16 Text 用户名: st_2 TextSize 12 Text 密码: st_3 TextSize 12

sle_1 Text sel_2 Text cb_1 Text 登录 cb_2 Text 取消

Title 系统登录 w_welcome WindowType Main!

在 Sle_2 的 Modify 事件中输入如下的程序代码:

cb_1.setfocus()

当用户在该单行编辑器中输入回车时触发该事件。该事件将程序的焦点转移到按钮控

件上,使按钮 cb_1 能够对回车事件做出响应。

Pow erBuilder9.0 基础应用与系统开发

— 394 —

在 cb_1 控件的 Clicked 事件中输入如下的程序代码:

string password,mm;

yhm=sle_1.text

mm=sle_2.text

select WSCGGL.RENYUAN.RY_RYXM ,WSCGGL.RENYUAN.RY_RYMM

into :ruxm, :password

from WSCGGL.RENYUAN

where WSCGGL.RENYUAN.RY_YHM=:yhm;

if password=mm then

open(w_main)

close(w_welcome)

else

messagebox("警告","输入的用户名或口令有误,请重新输入");

end if

在 cb_2 控件的 Clicked 事件中输入下面的代码:

close(w_welcome)

(4) 设计 C/S 方式下的主窗口。主窗口上放置了 1 个静态文本框控件和 7 个按钮控

件。按钮控件的单击事件用来打开具有不同模块功能的窗口。主窗口上控件布局如图 15-

14 所示。

图 15-14 C/S 方式下的主窗口

主窗口属性设置及控件的属性设置见表 15-11。

第 15 章 网上采购管理信息系统开发实例

— 395 —

表 15-11 主窗口中各个控件的属性设置表

控 件 属 性 属 性 值 Tex 采购管理系统 st_1 TextSize 12

cb_1 Text 计划平衡 cb_2 Text 比质比价 cb_3 Text 合同草本 cb_4 Text 合同审批 cb_5 Text 付款通知 cb_6 Text 付款查询 cb_7 Text 退 出

Title 采购管理系统 w_main WindowType Main!

在“计划平衡”按钮控件的 Clicked 事件中输入下面的代码:

open(w_jhph)

在“比质比价”按钮控件的 Clicked 事件中输入下面的代码:

open(w_bzbj)

在“合同草本”按钮控件的 Clicked 事件中输入下面的代码:

open(w_htsc)

在“合同审批”按钮控件的 Clicked 事件中输入下面的代码:

open(w_htsp)

在“付款通知”按钮控件的 Clicked 事件中输入下面的代码:

open(w_fktz)

在“付款查询”按钮控件的 Clicked 事件中输入下面的代码:

open(w_bktzcx)

在“退出”按钮控件的 Clicked 事件中输入下面的代码:

close(w_main)

15.3.2 B/S 下主窗口

为了顺利完成 B/S 下的主窗口系统开发,在创建应用程序对象之前先建立 DB Profile

和 EAServer Profile。EAServer Profile 的设置如图 15-15 所示。

下面设计 B/S 方式下的登录窗口。

(1) 单击菜单 File|New,选择 Web 标签下的 Web/JSP Page 画板,按照向导建立一个

名为 denglu 的 JSP 页面。向导生成一个 JSP 例子程序。用同样的方法生成 control.jsp。登

录页面如图 15-16 所示。

Pow erBuilder9.0 基础应用与系统开发

— 396 —

图 15-15 EAServer Perfile 的设置

图 15-16 登录页面

在 JSP 设计面板中,单击 Source 标签,添加如下的代码:

<%@ page contentType="text/html; charset=GBK" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

<HEAD>

<TITLE>登录</TITLE>

<META http-equiv="Content-Type" content="text/html; charset=gb2312">

</HEAD>

<BODY bgColor="#d0d2c4" PSPARAMS="">

第 15 章 网上采购管理信息系统开发实例

— 397 —

<BR>

<P align="center"><FONT color="#ff0000" size="6">网上采购系统</FONT></P>

<BR>

<P align="center"><FONT color="#ff0000" size="4">进 入 系 统 之 前 您 必 须 先 登 录

</FONT></P>

<P align="center"><FONT color="#ff0000" size="4">如 果 你 还 没 有 注 册 请 先 注 册

</FONT></P>

<P align="center"> </P>

<DIV align="center">

<FORM name="form" action="control.jsp" method="post">

<TABLE cellSpacing="0" cellPadding="0" width="375" border="0">

<TBODY>

<TR>

<TD width="110"><P align="right">用户名</P></TD>

<CENTER>

<TD width="259" height="30">

<INPUT size="21" name="username">

</TD>

</CENTER>

</TR>

<TR>

<TD width="110"><P align="right">密&nbsp; 码</P></TD>

<CENTER>

<TD width="259" height="30">

<INPUT type="password" size="21" name="password">

</TD>

</CENTER>

</TR>

<TR>

<TD width="369" colSpan="2" height="30">

<BR>

<P align="center">

<INPUT type="submit" value="登 陆" name="B1">

</P>

<P align="center"> </P>

<P align="center"> </P>

<P align="center"> </P>

</TD>

</TR>

</TBODY>

</TABLE>

</FORM>

</DIV>

</BODY>

</HTML>

用户输入用户名和口令后,提交到 control.jsp 页面进行处理。Control.jsp 的代码如下:

<%@ page contentType="text/html; charset=GBK" %>

<%@ page import="java.sql.*"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

Pow erBuilder9.0 基础应用与系统开发

— 398 —

<HTML>

<HEAD>

<TITLE>control</TITLE>

<META http-equiv="Content-Type" content="text/html; charset=gb2312">

</HEAD>

<BODY bgColor="white" PSSSEVTMODEL="" PSPARAMS="">

<%

String username=request.getParameter("username");

String password=request.getParameter("password");

out.println(username);

String sDBDriver="sun.jdbc.odbc.JdbcOdbcDriver";

String sConnStr="jdbc:odbc:wscg";

Connection conn=null;

ResultSet rs=null;

String sql="select * from renyuan";

try {

Class.forName(sDBDriver);//加载 JDBC-ODBC 驱动程序

conn=DriverManager.getConnection(sConnStr,"wscggl","123456");

Statement stmt=conn.createStatement();

rs=stmt.executeQuery(sql);

while(rs.next()) {

String yhm=rs.getString("ry_yhm");

String mm=rs.getString("ry_mm");

String rylx=rs.getString("ry_lx");

out.println("fdgdfg "+rylx);

if(yhm.equals(username)&&mm.equals(password)) {

if(rylx.equals("3")) //类型为 3 的人员为供应商

response.sendRedirect("main.jsp?flag=1");

else

response.sendRedirect("main.jsp?flag=0");

break;

}

}

}

catch(SQLException ex) {

System.out.println("sql_data.executeQuery:"+ex.getMessage());

}

%>

</BODY>

</HTML>

上面的代码实现了对用户进行验证,使不同类型的人员,限定进入不同的模块。

(2) 设计 B/S 方式下的主窗口。单击菜单 File|New,选择 Web 标签下的 Web/JSP Page

画板,按照向导建立一个命名为 main 的 JSP 页面。该页面根据登录用户显示不同的内

容,图 15-17 为供应商登录后显示的页面。

第 15 章 网上采购管理信息系统开发实例

— 399 —

图 15-17 供应商主页面

主页面根据 control.jsp 传送过来的 flag 标志确定显示不同的内容。代码如下:

<%

String flag=request.getParameter("flag");

if(flag.equals("0")) {

%>

<font size="3">您好,欢迎进入计划申报管理</font>

<%

}

else {

%>

<font size="3">您好,欢迎进入报价管理</font>

<%

}

%>

15.4 订单管理子系统

15.4.1 采购计划申报

采购计划申报模块实现用户在网上实施采购计划申报。假定已经创建了一个名为

p_cggl_webdw 的 Web DW Container,p_cggl_webdw 里包含计划申报和供应商报价所需要

的所有数据窗口(如何在 JSP 里使用数据窗口在第 9 章进行了介绍),并且 p_cggl_webdw

Pow erBuilder9.0 基础应用与系统开发

— 400 —

已经 Deploy 到 EAServer 中。

在计划申报模块中,需要建立 2 个数据窗口,d_jhsb 用来进行计划的录入,d_jhcx 用

来进行计划的查询。d_jhsb 数据窗口对象的布局如图 15-18 所示。d_jhcx 数据窗口对象的

布局如图 15-19 所示。

图 15-18 d_jhsb 数据窗口对象布局

图 15-19 d_jhcx 数据窗口对象布局

对应数据窗口,计划申报模块包含 2 个 JSP 页面:jhsb.jsp 和 jhcx.jsp。

(1) 创建 jhsb.jsp 页面。单击菜单 File|New,选择 Web 标签下的 Web/JSP DataWindow

Page 画板,按照向导建立一个名为 jhsb 的 JSP 页面。在选择 WebDW ComponentTypes

时,选择 WebDW Container,选择已经创建好的 p_cggl_webdw 容器。向导生成一个有数

据窗口的 JSP 例子程序。

第 15 章 网上采购管理信息系统开发实例

— 401 —

右击 jhsb.jsp 的 Page 标签,选择 Page Properties,弹出如图 15-20 所示的 Page 属性,

进行 Page 属性设置。

图 15-20 Page 属性设置

选中 WebDW 控件,右击选择 Sybase Web DataWindow DTC Properties,弹出图 15-21

所示的窗口,对 WebDW 控件进行设置。

图 15-21 WebDW 的属性设置

Pow erBuilder9.0 基础应用与系统开发

— 402 —

选择 Insert|Form Field|Push Button,添加名为 cb_insert,cb_update 和 cb_return 的按

钮。选中 cb_insert 按钮,选择 ServerAction()事件,添加如图 15-22 所示的代码。

图 15-22 cb_insert 按钮的 ServerAction()事件

同样,为 cb_update 按钮的 ServerAction()事件添加如下代码:

webDW. Update();

切换到源代码下,在<body></body>之间添加如下代码。

<BODY PSSSEVTMODEL="" PSPARAMS="" bgColor="#d0d2c4">

<TABLE height="52" cellSpacing="0" cellPadding="0" width="794"

border="0">

<TBODY>

<TR>

<TD width="288" height="52">

<IMG height="52" src="images/qtop5.jpg" width="326"

border="0">

</TD>

<TD class="text" vAlign="bottom" noWrap width="402" height="52">

&nbsp;&nbsp;&nbsp;

<FONT color="#0000ff" size="4">

<B>计划</B></FONT>

<FONT color="#0000ff"><FONT size="4"><B>管理</B>

</FONT>

</FONT>

</TD>

</TR>

</TBODY>

</TABLE>

<TABLE cellSpacing="0" cellPadding="0" width="100%" align="center"

border="0">

<TBODY>

<TR>

<TD width="1" bgColor="#615f64" height="25"><BR></TD>

<TD align="middle" background="images/qback2.gif">

<P align="left">

&nbsp;&nbsp;&nbsp; 您当前的位置是:

<FONT color="#0000ff" size="3">

计划管理→计划查询 </FONT> &nbsp;&nbsp;&nbsp;

<SPAN class="user_state_text">

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作员为:

<FONT color="#0000ff">王海</FONT>

第 15 章 网上采购管理信息系统开发实例

— 403 —

</SPAN>

</P>

</TD>

</TR>

</TBODY>

</TABLE>

<P>

</P>

<SCRIPT lang="JavaScript">

function home(){

window.location="main.jsp";

}

</SCRIPT>

<P align="center">

<INPUT style="WIDTH: 66px; HEIGHT: 24px" onclick="home()" type="button"

size="35" value="返 回" name="cb_return">

</P>

</BODY>

最后得到的页面如图 15-23 所示。

图 15-23 计划申报页面

(2) 创建 jhcx.jsp 页面。单击菜单 File|New,选择 Web 标签下的 Web/JSP DataWindow

Page 画板,按照向导建立一个名为 jhsb 的 JSP 页面。直接在 Source 标签里添加下面的代

码。

Pow erBuilder9.0 基础应用与系统开发

— 404 —

在头中加入对汉字的支持:

<%@ page contentType="text/html; charset=GBK" %>

在<body></body>之间加入

<BODY PSSSEVTMODEL="" PSPARAMS="" bgColor="#d0d2c4">

<TABLE height="52" cellSpacing="0" cellPadding="0" width="794"

border="0">

<TBODY>

<TR>

<TD width="288" height="52">

<IMG height="52" src="images/qtop5.jpg" width="326"

border="0">

</TD>

<TD class="text" vAlign="bottom" noWrap width="402" height="52">

&nbsp;&nbsp;&nbsp;

<FONT color="#0000ff" size="4">

<B>计划</B></FONT>

<FONT color="#0000ff"><FONT size="4"><B>管理</B>

</FONT>

</FONT>

</TD>

</TR>

</TBODY>

</TABLE>

<TABLE cellSpacing="0" cellPadding="0" width="100%" align="center"

border="0">

<TBODY>

<TR>

<TD width="1" bgColor="#615f64" height="25"><BR></TD>

<TD align="middle" background="images/qback2.gif">

<P align="left">

&nbsp;&nbsp;&nbsp; 您当前的位置是:

<FONT color="#0000ff" size="3">

计划管理→计划查询 </FONT> &nbsp;&nbsp;&nbsp;

<SPAN class="user_state_text">

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作员为:

<FONT color="#0000ff">王海</FONT>

</SPAN>

</P>

</TD>

</TR>

</TBODY>

</TABLE>

<P>

</P>

<SCRIPT lang="JavaScript">

function home(){

window.location="main.jsp";

}

</SCRIPT>

第 15 章 网上采购管理信息系统开发实例

— 405 —

<P align="center">

<INPUT style="WIDTH: 66px; HEIGHT: 24px" onclick="home()" type="button"

size="35" value="返 回" name="cb_return">

</P>

</BODY>

运行结果如图 15-24 所示。

图 15-24 计划查询页面

15.4.2 计划平衡

计划平衡模块实现采购计划的汇总平衡。计划平衡模块由一个大的窗口组成,在窗口

的左上部分显示待平衡的计划信息,窗口的右上部分显示选中的需要平衡的计划信息。下

半部分显示平衡后的平衡信息。该窗口由主窗口上的“计划平衡”按钮单击事件触发。计

划平衡管理窗口界面如图 15-25 所示。

该窗口中的数据显示是由数据窗口控件来完成的。该窗口有 3 个数据窗口控件

dw_1、dw_2、dw_3,分别连接数据窗口对象 d_jhsb、d_jh、d_ph。该窗口中的文字显示

是由静态文本框来完成,共有 3 个静态文本框控件。在窗口的中间放置了 4 个按钮控件,

其中 cb_1 按钮 Click 事件实现从待平衡的计划表中选取相同物资编码的所有计划;cb_2

按钮 Click 事件实现去掉已经选好的计划,放回到待平衡计划中;cb_3 按钮 Click 事件实

现全部去掉已经选好的计划,放回到待平衡计划中;cb_4 按钮 Click 事件实现计划的汇总

平衡。

Pow erBuilder9.0 基础应用与系统开发

— 406 —

图 15-25 计划平衡窗口

整个窗口上控件的属性设置见表 15-12。

表 15-12 计划平衡窗口中各个控件的属性设置

控 件 属 性 属性值 Text 待平衡的计划表: st_1 TextSize 12 DataObject d_jhsb

dw_1 BorderStyle Stylelowered! Text 选中的计划表: st_2 TextSize 12 DataObject d_jh

dw_2 BorderStyle Stylelowered!

cb_1 Text >> cb_2 Text << cb_3 Text 重置

Text 平衡表: st_3 TextSize 12

cb_4 Text 平衡 cb_5 Text 保存

DataObject d_ph dw_3

BorderStyle Stylelowered! Title 计划平衡

w_jhph WindowType Main!

在计划平衡管理窗口的 Open 事件中输入如下的程序代码:

dw_1.settransobject(sqlca); //定义 dw_1 的事务对象

dw_2.settransobject(sqlca); //定义 dw_2 的事务对象

dw_3.settransobject(sqlca); //定义 dw_3 的事务对象

第 15 章 网上采购管理信息系统开发实例

— 407 —

dw_1.retrieve(); //执行查询,显示数据

在计划管理窗口的变量定义窗口中定义系统的实例变量,代码如下:

String wzbm; //物资编码

String jhbh; //计划编号

在 dw_1 数据窗口的 Click 事件中输入如下的程序代码:

if row>0 and row<=dw_1.rowcount() then //如果选择的行在范围内

dw_1.selectrow(0,false) //数据窗口中所有行被撤销

dw_1.selectrow(row,true) //保持行被选中状态

wzbm=dw_1.object.jihua_jh_cgwzbm[row] //记录选中行的物资编码值

end if

在 cb_1 按钮的 Click 事件中输入如下的程序代码:

//过滤数据窗口 dw_1 的数据,规则为物资编码不等于选中的物资编码

dw_1.setfilter("jihua_jh_cgwzbm<>’"+wzbm+"’");

dw_1.filter(); //执行过滤

//过滤数据窗口 dw_2 的数据,规则为物资编码等于选中的物资编码

dw_2.setfilter("jh_cgwzbm=’"+wzbm+"’");

dw_2.filter(); //执行过滤

dw_1.retrieve() //执行过滤后的查询

dw_2.retrieve() //执行过滤后的查询

在 dw_2 窗口的 Click 事件中输入如下的程序代码:

if row>0 and row<=dw_2.rowcount() then //如果选择的行在范围内

dw_2.selectrow(0,false) //数据窗口中所有行被撤销

dw_2.selectrow(row,true) //保持行被选中状态

jhbh=dw_2.object.jh_jhbh[row] //记录选中行的计划编号

end if

在 cb_2 按钮的 Click 事件中输入如下的程序代码:

int i

String ls_jhbh //ls_jhbh 存放计划编号

String dw1_where //dw1_where 存放 dw1 的过滤器的条件

String dw2_where //dw2_where 存放 dw2 的过滤器的条件

dw1_where=""

dw2_where=""

if dw_2.rowcount()>0 then //dw2 有记录时

ls_jhbh=""

if dw2row >0 then //选中一条记录时

for i=1 to dw_2.rowcount()

ls_jhbh=dw_2.object.jh_jhbh[i]

if ls_jhbh=jhbh then

dw2_where=dw2_where+" and (jh_jhbh<>’"+ls_jhbh+"’)"

else

dw1_where=dw1_where+" and

(jihua_jh_jhbh<>’"+ls_jhbh+"’)"

Pow erBuilder9.0 基础应用与系统开发

— 408 —

end if

next

dw_1.setfilter(dw1_where); //设置过滤条件

dw_1.filter(); //执行过滤

dw_2.setfilter("jh_cgwzbm=’"+wzbm+"’"+dw2_where); //设置过滤条件

dw_2.filter(); //执行过滤

dw_1.retrieve() //刷新数据窗口

dw_2.retrieve() //刷新数据窗口

else

messagebox("错误","请选择需要取消的计划")

end if

else

messagebox("错误","没有需要取消的计划")

end if

在 cb_3 按钮的 Click 事件中输入如下的程序代码:

dw_2.reset() //清除 dw2 的数据

dw_3.reset() //清除 dw2 的数据

dw_1.setfilter("") //清除过滤条件

dw_1.filter() //执行清除过滤

dw_1.retrieve() //恢复原来数据

在 cb_4 按钮的 Click 事件中输入如下的程序代码:

int dw3row,i // dw3row 记录插入行

double kcsl,jhcgsl,sjcgsl

datetime xqrq

date dd //定义事件变量

time tt

string ls_date

if dw_2.rowcount()=0 then

messagebox(’注意:’,’先选择需要平衡的计划’)

return

end if

dd=today() //取得系统时间

tt=now() //

dw_3.reset() //清空数据窗口

dw3row=dw_3.insertrow(0) //插入一条新记录

dw_3.object.ph_cgwzbm[dw3row]=wzbm //赋值

dw_3.object.ph_cbr[dw3row]=ryxm //赋值

select WSCGGL.WUZI.KCSL

into :kcsl

from WSCGGL.WUZI

where WSCGGL.WUZI.WZBM=:wzbm ; //查找物资表,获得库存数量

dw_3.setitem(dw3row,"ph_kcsl",kcsl) //赋值

ls_date=string(dd,"yyyymmdd")

dw_3.setitem(dw3row,"ph_phrq",datetime(dd,tt)) //赋值

//计算最小需求日期和总采购数量

xqrq=dw_2.object.cgwuzi_cg_xqrq[1] //赋初值

jhcgsl=0 //赋初值

第 15 章 网上采购管理信息系统开发实例

— 409 —

for i=1 to dw_2.rowcount()

jhcgsl=jhcgsl+dw_2.object.cgwuzi_cg_xqsl[i]

if xqrq>dw_2.object.cgwuzi_cg_xqrq[i] then

xqrq=dw_2.object.cgwuzi_cg_xqrq[i]

end if

next

dw_3.setitem(dw3row,"ph_xqrq",xqrq) //赋值

dw_3.setitem(dw3row,"ph_jhcgsl",jhcgsl) //赋值

sjcgsl=jhcgsl - kcsl

dw_3.setitem(dw3row,"ph_sjcgsl",sjcgsl) //赋值

//生成平衡号

string maxxh,ls_phbh

integer li_lsh

ls_date="%" + ls_date+ "%" //设定 where 条件

select max(WSCGGL.PINGHEN.PH_PHBH)

into :maxxh

from WSCGGL.PINGHEN

where WSCGGL.PINGHEN.PH_PHBH like :ls_date ; //查找平衡表

if isnull(maxxh) then //如果当天没有平衡信息,序号为 001

maxxh="000"

else

maxxh=right(maxxh,3) //取平衡编号最右的 3 个字符

end if

li_lsh=integer(maxxh) //转换为整型

if len(string(li_lsh+1))=1 then // 最大序号加 1

maxxh="00" + string(li_lsh+1)

elseif len(string(li_lsh+1))=2 then

maxxh="0" + string(li_lsh+1)

elseif len(string(li_lsh+1))=3 then

maxxh=string(li_lsh+1)

end if

ls_phbh="w01" + string(dd,"yyyymmdd") + maxxh //赋值

dw_3.setitem(dw3row,"ph_phbh",ls_phbh) //赋值

在 cb_5 按钮的 Click 事件中输入如下的程序代码:

if dw_3.update()=1 then

commit;

messagebox("保存成功","保存成功!!")

end if

15.5 报价比价子系统

15.5.1 网上报价

网上报价模块实现供应商根据平衡的物资信息进行网上报价。网上报价需要的数据窗

口为 d_bj 和 d_bjxx。d_bj 数据窗口对象的布局如图 15-26 所示。d_bjxx 数据窗口对象的

布局如图 15-27 所示。

Pow erBuilder9.0 基础应用与系统开发

— 410 —

图 15-26 d_bj 数据窗口

图 15-27 d_bjxx 数据窗口

采购计划申报模块包含 2 个 JSP 页面:gysbj.jsp 和 bjcx.jsp。

(1) 创 建 gysbj.jsp 页 面 。 单 击 菜 单 File|New , 选 择 Web 标 签 下 的 Web/JSP

DataWindow Page 画板,按照向导建立一个名为 gysbj 的 JSP 页面。

在 Source 标签中添加下面的代码。

<%@ page contentType="text/html; charset=GBK" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

<HEAD>

<META HTTP-EQUIV="PowerSiteData" NAME="SERVERLANGUAGE"

CONTENT="Java">

<TITLE>gysbj</TITLE>

<META http-equiv="Content-Type" content="text/html; charset=gb2312">

<META content="PB9.0.0.5507" name="GENERATOR">

第 15 章 网上采购管理信息系统开发实例

— 411 —

</HEAD>

<BODY bgColor="#d0d2c4" PSPARAMS="" PSSSEVTMODEL="">

<TABLE height="52" cellSpacing="0" cellPadding="0" width="794"

border="0">

<TBODY>

<TR>

<TD width="288" height="52">

<IMG height="52" src="images/qtop5.jpg" width="326"

border="0">

</TD>

<TD class="text" vAlign="bottom" noWrap width="402" height="52">

&nbsp;&nbsp;&nbsp;

<FONT color="#0000ff" size="4"> <B>报价</B></FONT>

<FONT color="#0000ff"><FONT size="4"><B>管理</B></FONT>

</FONT>

</TD>

</TR>

</TBODY>

</TABLE>

<TABLE cellSpacing="0" cellPadding="0" width="100%" align="center"

border="0">

<TBODY>

<TR>

<TD width="1" bgColor="#615f64" height="25"><BR></TD>

<TD align="middle" background="images/qback2.gif">

<P align="left">

&nbsp;&nbsp;&nbsp; 您当前的位置是:

<FONT color="#0000ff" size="3">

采购管理→报价</FONT>&nbsp;&nbsp;&nbsp;

<SPAN class="user_state_text">

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作员为:

<FONT color="#0000ff">王海</FONT>

</SPAN>

</P>

</TD>

</TR>

</TBODY>

</TABLE>

<P>

<SCRIPT lang="JavaScript">

function home(){

window.location="bjcx.jsp";

}

</SCRIPT>

</P>

<P align="center">

<INPUT language="JavaScript" style="WIDTH: 69px; HEIGHT: 26px"

type="button" size="36" value="插 入" name="cb_insert" PSSERVERSCRIPTABLE>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<INPUT language="JavaScript" style="WIDTH: 69px; HEIGHT: 26px"

Pow erBuilder9.0 基础应用与系统开发

— 412 —

type="button" size="36" value=" 保 存 " name="cb_save" PSSERVERSCRIPTABLE>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<INPUT style="WIDTH: 66px; HEIGHT: 24px" onclick="home()" type="button"

size="35" value="返 回" name="cb_return">

</P>

</BODY>

</HTML>

以上代码省略了 PowerBuilder 9.0 自动生成的代码。程序运行结果如图 15-28 所示。

图 15-28 报价页面

(2) 创建 bjcx.jsp 页面。单击菜单 File|New,选择 Web 标签下的 Web/JSP DataWindow

Page 画板,按照向导建立一个名为 bjcx 的 JSP 页面。

在 Source 标签中添加下面的代码。

<%@ page contentType="text/html; charset=GBK" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

<HEAD>

<META HTTP-EQUIV="PowerSiteData" NAME="SERVERLANGUAGE"

CONTENT="Java">

<TITLE>bjcx</TITLE>

<META http-equiv="Content-Type" content="text/html; charset=gb2312">

<META content="PB9.0.0.5507" name="GENERATOR">

</HEAD>

<BODY bgColor="#d0d2c4" PSPARAMS="" PSSSEVTMODEL="">

<TABLE height="52" cellSpacing="0" cellPadding="0" width="794"

border="0">

<TBODY>

第 15 章 网上采购管理信息系统开发实例

— 413 —

<TR>

<TD width="288" height="52">

<IMG height="52" src="images/qtop5.jpg" width="326"

border="0">

</TD>

<TD class="text" vAlign="bottom" noWrap width="402" height="52">

&nbsp;&nbsp;&nbsp;

<FONT color="#0000ff" size="4"><B>报价</B></FONT>

<FONT color="#0000ff">

<FONT size="4"><B>管理</B></FONT>

</FONT>

</TD>

</TR>

</TBODY>

</TABLE>

<TABLE cellSpacing="0" cellPadding="0" width="100%" align="center"

border="0">

<TBODY>

<TR>

<TD width="1" bgColor="#615f64" height="25"><BR></TD>

<TD align="middle" background="images/qback2.gif">

<P align="left">

&nbsp;&nbsp;&nbsp; 您当前的位置是:

<FONT color="#0000ff" size="3">采购管理→报价查询</FONT>

&nbsp;&nbsp;&nbsp;

<SPAN class="user_state_text">

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作员为:

<FONT color="#0000ff">王海</FONT>

</SPAN>

</P>

</TD>

</TR>

</TBODY>

</TABLE>

<P>

</P>

<SCRIPT lang="JavaScript">

function home(){

window.location="bjcx.jsp";

}

</SCRIPT>

<P align="center">

<INPUT style="WIDTH: 66px; HEIGHT: 24px" onclick="home()" type="button"

size="35" value="返 回" name="cb_return">

</P>

</BODY>

</HTML>以上代码省略了 PowerBuilder 9.0 自动生成的代码。程序运行结果如图 15-

29 所示。

Pow erBuilder9.0 基础应用与系统开发

— 414 —

图 15-29 报价查询页面

15.5.2 比价与草签合同

比价模块实现采购员对供应商的报价进行比较,选择合适的供应商,生成合同草稿。

在窗口的左上部分显示平衡信息,窗口的右上部分显示供应商的报价信息,下半部分显示

选中的报价信息。该窗口由主窗口上的“比质比价”按钮单击事件触发。

比价管理窗口界面如图 15-30 所示。

图 15-30 比质比价窗口

第 15 章 网上采购管理信息系统开发实例

— 415 —

该窗口中共有 3 个数据窗口控件 dw_1、dw_2、dw_3,分别连接数据窗口对象

d_ph、d_bjxx、d_bj2。该窗口中的文字显示是由静态文本框来完成,共有 3 个静态文本

框控件。在数据窗口 dw_1 和 dw_2 中间放置了 1 个按钮 cb_1,在 dw_1 和 dw_2 的下面

放置了 1 个按钮 cb_2,在窗口的下面放置了 1 个按钮 cb_3。其中 cb_1 按钮用于实现从供

应商报价表中选取用户选择的平衡相关的报价信息;cb_2 按钮用于实现把用户选择的报

价信息放到 dw_3 数据窗口中;cb_3 按钮 Click 事件实现根据报价信息和平衡信息生成合

同草本,并且打开 w_htsc 窗口。

整个窗口上控件的属性设置见表 15-13。

表 15-13 比质比价窗口中各个控件的属性设置表

控 件 属 性 属性值 Text 平衡表信息: st_1 TextSize 12 DataObject d_ph

dw_1 BorderStyle Stylelowered!

cb_1 Text >> Text 报价信息: st_2 TextSize 12 DataObject d_bjxx

dw_2 BorderStyle Stylelowered!

cb_2 Text 选中 DataObject d_bj2

dw_3 BorderStyle Stylelowered!

cb_3 Text 生成合同 Title 比质比价 w_bzbj WindowType Main!

合同草本窗口如图 15-31 所示。窗口里有一个数据窗口和一个静态文本框。

图 15-31 合同草本窗口

窗口上控件的属性设置见表 15-14。

Pow erBuilder9.0 基础应用与系统开发

— 416 —

表 15-14 合同草本窗口中各个控件的属性设置表

控 件 属 性 属性值 Text 合同信息 st_1 TextSize 18 DataObject d_htlr dw_1 BorderStyle Stylelowered! Title 合同草本 w_htsc WindowType Main!

在比质比价窗口的 Open 事件中输入如下的程序代码:

dw_1.settransobject(sqlca); //定义 dw_1 的事务对象

dw_2.settransobject(sqlca); //定义 dw_2 的事务对象

dw_3.settransobject(sqlca); //定义 dw_3 的事务对象

dw_1.retrieve(); //执行查询,显示数据

在比质比价窗口的变量定义窗口中定义系统的实例变量,代码如下:

string phbh //平衡编号

string bjxh //报价序号

在 dw_1 数据窗口的 Click 事件中输入如下的程序代码:

if row>0 and row<=dw_1.rowcount() then //如果选择的行在范围内

dw_1.selectrow(0,false) //数据窗口中所有行被撤销

dw_1.selectrow(row,true) //保持行被选中状态

phbh=dw_1.object.ph_phbh[row] //记录选中行的平衡编号

end if

在 cb_1 按钮的 Click 事件中输入如下的程序代码:

dw_2.setfilter("baojia_bj_phbh=’"+phbh+"’"); //设置过滤条件

dw_2.filter(); //执行数据过滤

dw_2.retrieve() //更新数据窗口

在 dw_2 数据窗口的 Click 事件中输入如下的程序代码:

if row>0 and row<=dw_2.rowcount() then //如果选择的行在范围内

dw_2.selectrow(0,false) //数据窗口中所有行被撤销

dw_2.selectrow(row,true) //保持行被选中状态

bjxh=dw_2.object.baojia_bj_xh[row] //记录选中行的平衡编号

end if

在 cb_2 按钮的 Click 事件中输入如下的程序代码:

dw_3.setfilter("baojia_bj_xh=’"+bjxh+"’"); //设置过滤条件

dw_3.filter(); //执行数据过滤

dw_3.retrieve() //更新数据窗口

在 cb_3 按钮的 Click 事件中输入如下的程序代码:

int ii datetime dhrq,qdrq

第 15 章 网上采购管理信息系统开发实例

— 417 —

date dd time tt string cgwz,yfqdr,yfdw,jfdw double hkzj dd=today() //取系统日期 tt=now() //取系统时间 qdrq=datetime(dd,tt) jfdw=’昆仑油田物资处’ SELECT WSCGGL.BAOJIA.BJ_GYSBH, //从报价信息里获得合同信息 WSCGGL.BAOJIA.BJ_BJZJ, WSCGGL.BAOJIA.BJ_DHRQ, WSCGGL.BAOJIA.BJ_CGWZ INTO :yfdw ,:hkzj ,:dhrq , :cgwz FROM WSCGGL.BAOJIA WHERE WSCGGL.BAOJIA.BJ_XH=:bjxh ; SELECT WSCGGL.GONGYINGSHANG.GYS_FZR //获得乙方负责人 INTO :yfqdr FROM WSCGGL.GONGYINGSHANG WHERE WSCGGL.GONGYINGSHANG.GYS_GYSMC=:yfdw ; string maxxh,ls_date integer li_lsh ls_date="%" + string(dd,"yyyymmdd") + "%" SELECT max(WSCGGL.HETONG.HT_HTBH) //取出合同序号 INTO :maxxh FROM WSCGGL.HETONG WHERE WSCGGL.HETONG.HT_HTBH like :ls_date ; if isnull(maxxh) then maxxh="000" else maxxh=right(maxxh,3) //取平衡编号最右的 3 个字符 end if li_lsh=integer(maxxh) //把序号转换为整型 if len(string(li_lsh+1))=1 then //最大序号加 1 maxxh="00" + string(li_lsh+1) elseif len(string(li_lsh+1))=2 then maxxh="0" + string(li_lsh+1) elseif len(string(li_lsh+1))=3 then maxxh=string(li_lsh+1) end if htbh="w" + string(dd,"yyyymmdd") + "459" + maxxh //赋值 INSERT INTO WSCGGL.HETONG //插入一条合同信息 VALUES (:htbh,:qdrq,:dhrq,:jfdw,:ryxm,:yfdw,:yfqdr,:cgwz,:hkzj,’0’) ; close(w_bzbj) //关闭窗口 w_bzbj

open(w_htcb) //打开窗口 w_htcb

15.6 合同审核子系统

15.6.1 主窗口

合同审核模块用于对合同草稿进行审批。在本系统中,假定合同必须通过 3 个部门人

Pow erBuilder9.0 基础应用与系统开发

— 418 —

员的审批才能正式签订合同。为此在人员表中设置了人员类型字段。值为“11”的人员先

进行审批,然后值为“12”的人员再审批,最后才是值为“13”的人员审批。

主窗口有一个数据窗口控件 dw_1,其连接数据窗口对象 d_htlr。该窗口中的文字显

示是由静态文本框来完成,共有 6 个静态文本框控件。在每个审批意见静态文本框下面都

放置了一个下拉框控件,在每个备注说明静态文本框下面都放置了一个多行文本框控件。

在窗口的下面放置了两个按钮,其中 cb_1 按钮实现插入一条审批信息,cb_2 按钮实现关

闭 w_htsp 窗口。

合同审批窗口界面如图 15-32 所示。

图 15-32 合同审批主窗口

合同审批主窗口上控件的属性设置见表 15-15。

表 15-15 合同审批窗口中各个控件的属性设置表

控 件 属 性 属性值 Text 合同信息 st_1 TextSize 12 DataObject d_htlr

dw_1 BorderStyle Stylelowered!

st_2 Text 业务主管意见: Text 同意 ddplb_1 Text 不同意

st_3 Text 合同管理人意见: Text 同意

ddplb_2 Text 不同意

st_4 Text 主管领导意见:

第 15 章 网上采购管理信息系统开发实例

— 419 —

(续表)

控 件 属 性 属性值 Text 同意

ddplb_3 Text 不同意

st_5 Text 备注说明: mle_1 Text st_6 Text 备注说明: mle_2 Text st_7 Text 备注说明: mle_3 Text cb_1 Text 确定 cb_2 Text 取消

Title 合同审批 w_htsp WindowType Main!

15.6.2 合同审批业务实现

合同审批管理首先查找合同表,把该业务员需要审批的合同全部列出来,业务员选择

一个合同进行审批,填写完审批意见后提交,系统自动产生审批记录,保存到审批记录表

中,同时修改合同标志。

在主窗口的 Open 事件中输入如下的程序代码:

string bz

integer sx

bz=right(rylx,1) //获得审批顺序

sx=integer(bz)

bz=string(sx - 1) //确定合同标志

dw_1.settransobject(sqlca); //定义 dw_1 的事务对象

dw_1.setfilter("ht_bz=’" + bz +"’"); //设置过滤器

dw_1.filter(); //执行过滤

dw_1.retrieve(); //更新数据窗口

在 dw_1 数据窗口的 Click 事件中输入如下的程序代码:

htbh=dw_1.object.ht_htbh[row] //获得合同

在 cb_1 按钮的 Click 事件中输入如下的程序代码:

string spyj,bz ,spsx

int dwrow

datetime sprq

date dd

time tt

dd=today() //取系统日期

tt=now() //取系统时间

sprq=datetime(dd,tt) //获得审批日期

spsx=right(rylx,1) //获得审批顺序

if rylx=’11’ then //根据人员类型获得审批意见和备注

spyj=ddplb_1.text

bz=mle_1.text

elseif rylx=’12’ then

spyj=ddplb_2.text

Pow erBuilder9.0 基础应用与系统开发

— 420 —

bz=mle_2.text

elseif rylx=’13’ then

spyj=ddplb_3.text

bz=mle_3.text

else

return

end if

string xh

SELECT max(WSCGGL.SHENPI.SP_XH) //获得当前最大序号

INTO :xh

FROM WSCGGL.SHENPI ;

if isnull(xh) then //序号为空,赋初值

xh="000000"

end if

integer li_lsh

li_lsh=integer(xh) //把序号转换成整型数据

xh="000000" + string(li_lsh+1) //序号加 1,并且转换成字符型

xh=right(xh,6) //重新赋值

INSERT INTO WSCGGL.SHENPI //往审批记录表插入一条审批记录

VALUES(:xh, :htbh, :sprq, :ryxm, :spyj, :bz, :spsx) ;

UPDATE WSCGGL.HETONG //更新合同表的 bz

SET WSCGGL.HETONG.HT_BZ = :spsx

WHERE WSCGGL.HETONG.HT_HTBH = :htbh ;

close(w_htsp) //关闭合同审批窗口

在 cb_2 按钮的 Click 事件中输入如下的程序代码:

close(w_htsp) //关闭合同审批窗口

15.7 付款子系统

15.7.1 付款通知单生成

付款通知模块实现采购员根据发票信息,填写付款通知单,通知财务部门付款。本例

只实现采购员根据合同信息和供应商信息自动生成付款通知单,并且根据查询条件查询付

款通知单。

付款通知窗口有两个数据窗口控件 dw_1、dw_2,分别连接数据窗口对象 d_ht、

d_fktz。该窗口中的文字显示是由静态文本框来完成。在两个数据窗口中间放置了一个按

钮,按钮的 Click 事件实现根据所选择的合同信息生成一条付款通知单,并把它保存到数

据库中。

付款通知窗口界面如图 15-33 所示。

付款通知窗口上控件的属性设置见表 15-16。

第 15 章 网上采购管理信息系统开发实例

— 421 —

图 15-33 付款通知窗口

表 15-16 付款通知窗口中各个控件的属性设置表

控 件 属 性 属性值 Text 合同信息: st_1 TextSize 12 DataObject d_ht

dw_1 BorderStyle Stylelowered! Text 生成付款通知 cb_1 TextSize 12 DataObject d_fktz

dw_2 BorderStyle Title 付款通知 w_bktz WindowType Main!

在主窗口的变量定义窗口中定义系统的实例变量,代码如下:

string yfdw; //乙方单位

在付款通知窗口的 Open 事件中输入如下的程序代码:

dw_1.settransobject(sqlca); //定义 dw_1 的事务对象

dw_2.settransobject(sqlca); //定义 dw_2 的事务对象

dw_1.setfilter("ht_bz=’3’"); //设置过滤器,合同已经审核通过才可以付款

dw_1.filter(); //执行过滤

dw_1.retrieve(); //更新数据窗口

在 dw_1 数据窗口的 Click 事件中输入如下的程序代码:

if row>0 and row<=dw_2.rowcount() then //如果选择的行在范围内

dw_1.selectrow(0,false) //数据窗口中所有行被撤销

Pow erBuilder9.0 基础应用与系统开发

— 422 —

dw_1.selectrow(row,true) //保持行被选中状态

htbh=dw_1.object.ht_htbh[row] //记录选中行的合同编号

end if

在 cb_1 按钮的 Click 事件中输入如下的程序代码:

int dwrow

string sh,maxxh,ls_date,fkbh

datetime lrrq

date dd

time tt

dd=today() //获得系统日期

tt=now() //获得系统时间

lrrq=datetime(dd,tt) //录入日期赋值

dw_2.reset() //清空数据窗口

dwrow=dw_2.insertrow(0) //插入一条记录

SELECT WSCGGL.GONGYINGSHANG.GYS_SH //查找库表获得税号

INTO :sh

FROM WSCGGL.GONGYINGSHANG

WHERE WSCGGL.GONGYINGSHANG.GYS_GYSMC = :yfdw ;

ls_date="%" + string(dd,"yyyymmdd") + "%" //设定 like 条件

SELECT max(WSCGGL.FUKUAN.FK_FKTZBH) //查找库表获得最大编号

INTO :maxxh

FROM WSCGGL.FUKUAN

WHERE WSCGGL.FUKUAN.FK_FKTZBH LIKE :ls_date ;

if isnull(maxxh) then //如果找不到,最大序号为 0

maxxh="000"

end if

integer temp

maxxh=right(maxxh,3) //最大序号赋值

temp=integer(maxxh) //转换为整型

maxxh="000" + string(temp+1) //序号加 1,然后转换成字符型

maxxh=right(maxxh,3) //再赋值序号

fkbh=string(dd) + maxxh //获得付款编号

dw_2.object.fk_fktzbh[dwrow]=fkbh //数据窗口赋值

dw_2.object.fk_htbh[dwrow]=htbh //数据窗口赋值

dw_2.object.fk_yjdwmc[dwrow]=yfdw //数据窗口赋值

dw_2.object.fk_sh[dwrow]=sh //数据窗口赋值

dw_2.object.fk_lrrq[dwrow]=lrrq //数据窗口赋值

dw_2.object.fk_cbr[dwrow]=ryxm //数据窗口赋值

if dw_2.update()=1 then

commit; //提交数据,完成插入一条记录

messagebox("成功生成通知单","成功生成付款通知单!!")

end if

15.7.2 付款通知单查询

付款通知单查询模块实现用户选择查询条件查找符合条件的付款通知单。付款通知单

查询由一个大的窗口组成,在窗口的上半部分显示用户可选择的查询条件,下半部分显示

根据查询条件查找出的付款通知单。该窗口由主窗口上的“付款通知单查询”按钮单击事

第 15 章 网上采购管理信息系统开发实例

— 423 —

件触发。

付款通知查询窗口界面如图 15-34 所示。

图 15-34 付款通知单查询窗口

该窗口中的数据显示由数据窗口控件来完成。该窗口只有一个数据窗口控件 w_1,它

连接数据窗口对象 d_fktz。该窗口中的文字显示由静态文本框来完成,共有两个静态文本

框控件,用来显示“请选择查询条件”和“查询结果”。窗口中有 3 个 checkbox 控件,用

来提供给用户选择查询条件。“查询”按钮实现根据用户选择的查询条件查询库表。

整个窗口上控件的属性设置见表 15-17。

表 15-17 付款通知单查询窗口中各个控件的属性设置表

控 件 属 性 属性值 st_1 Text 请选择查询条件 TextSize 18 cbx_1 Text 承办人: cbx_2 Text 供应商: cbx_3 Text 编卡日期: sle_1 Text sle_2 Text sle_3 Text sle_4 Text cb_1 Text 查 询 TextSize 12 dw_1 DataObject d_fktz BorderStyle Stylelowered! w_bktzcx Title 编卡通知查询 WindowType Main!

付款通知单查询窗口的 Open 事件中输入如下的程序代码:

dw_1.settransobject(sqlca); //定义 dw_1 的事务对象

“查询”按钮的 Click 事件实现通过给定的查询条件查找库表,列出符合的结果集。

Pow erBuilder9.0 基础应用与系统开发

— 424 —

其程序代码如下:

string ls_where

string str1,str2,str3

date dd

if cbx_1.checked=true then //选择查询条件为承办人,设定 where 条件

if isnull(sle_1.text) then

messagebox("错误提示","请选择承办人!")

return

end if

str1=" fk_cbr = ’" +sle_1.text+ "’"

end if

if cbx_2.checked=true then //选择查询条件为供应商,设定 where 条件

if isnull(sle_2.text) then

messagebox("错误提示","请选择供应商!")

return

end if

if cbx_1.checked=true then //如果承办人已经选择

str2=" and fk_yjdwmc like ’" +sle_2.text+ "%’"

else //如果承办人没有被选择

str2="fk_yjdwmc like ’" +sle_2.text+ "%’"

end if

end if

if cbx_3.checked=true then //选择查询条件为编卡日期,设定 where 条件

if isnull(sle_3.text) then

messagebox("错误提示","请选择编卡日期!")

return

end if

if cbx_1.checked=true or cbx_2.checked=true then //如果承办人或供应商已选

str3=" and fk_fk_lrrq>’" +sle_3.text+ "’" + " and fk_fk_lrrq < ’"

+sle_4.text+ "’"

else //如果承办人和供应商都没有被选择

str3="fk_fk_lrrq>’" +sle_3.text+ "’" + " and fk_fk_lrrq < ’"

+sle_4.text+ "’"

end if

end if

ls_where=str1 +str2 + str3 //查询条件汇总

dw_1.setfilter(ls_where); //设定过滤器

dw_1.filter(); //执行过滤器

dw_1.retrieve() //更新数据窗口

15.8 小结

到此,一个完整的网上采购管理系统就创建完毕了。在本例中详细叙述了网上采购管

理系统的系统设计、计划申请、计划平衡、供应商报价、比质比价、合同生成、合同审

批、付款通知等子系统的设计。

本系统其他子系统中的窗口和上述窗口类似,由于篇幅所限,不再赘述。

第 16 章 仓储管理信息系统开发实例

16.1 系统设计

16.1.1 Target(目标)设计

系统开发的总体 Target(目标)是用计算机管理仓储业务从入库到出库的全部过程,为

企业的仓储管理工作提供高效、简洁、方便的服务。这里针对的是大中型企业的仓库管理

业务。

16.1.2 开发设计理念

仓储业务是企业供应链管理系统中非常重要的环节,对于任何具有购、销、存业务的

企业单位来说,它的许多业务都涉及到仓储管理,包括采购入库、质量检查、领料出库、

库管记账等。因此对于采购员和仓库管理员来说,每天很重要的一项工作就是处理涉及物

资管理的各种单据,例如:入库单、出库单、新到料日志、质量检验单等。原来依靠手工

记账、填写单据的管理方式工作效率低下,容易出现错误,查询有关数据困难,特别是难

于对库存物品的时机和数量进行优化。

在网络技术发展迅速的今天,要提升企业的竞争力,提高企业的效率,仓储管理工作

应当建立在高效、安全、快速的系统上。仓储管理系统可以加快信息流通的速度,通过将

工作流程自动化,减少业务处理过程的成本,使库存和资金占用量尽可能最少,同时提高

采购工作的科学性和规范性,减少盲目性。

16.1.3 开发运行环境

操作系统:Microsoft Windows 2000 Server

数据库:Oracle 9.2.1

开发工具:PowerBuilder 9.0

16.1.4 功能分析与模块设计

仓储管理业务包含系统设置、入库管理、出库管理、库存管理等内容。

系统设置主要为完成出入库业务提供所需的基本信息,包括物资编码、机构设置、仓

库设置、人员设置、部门设置、入库来源、出库用途。

入库管理:有关人员根据铁路运单将物资运回或由汽车运送到库房,库管人员填写新

到料日志,然后结合合同与其他人员对该物资进行验收并填写验收记录;当收到采购人员

开具的收料单和发票后,正式入库,修改库存。其内容包括:新到料日志的填写、质检验

收记录的填写,以及入库单单据的填写。

Pow erBuilder9.0 基础应用与系统开发

— 426 —

出库管理:当收到业务科室开具的移拨单,确认后才正式出库,修改库存;然后填写

仓库发料日志,并给领料人开出门证。其内容包括:出库单填写和出库单审核。

库存管理:主要完成对日常出入库业务的查询,数据的定期备份,以及库存管理中的

报表统计工作。

系统维护:更换操作员、更改口令等。

在系统功能分析的基础上,给出系统的功能模块图,如图 16-1 所示。

仓储管理系统

图 16-1 系统模块图

16.2 数据库设计

数据库是应用程序的中轴,数据库设计和维护的质量将在很大程度上决定应用程序的

质量和成功与否。通过对业务的调查,获得用户对数据库的信息要求、处理要求和安全

性、完整性的要求,从而建立本系统的数据流图,如图 16-2 所示。

16.2.1 概念设计

在需求分析阶段,得到了仓储管理系统的数据流程分析,接下来进行概念结构设计,

它是整个数据库设计的关键,独立于数据库逻辑结构、物理结构和 DBMS,其主要特点

如下。

第 16 章 仓储管理信息系统开发实例

— 427 —

用户

用户

合同编号

物资信息 入库单信息

入除单 信息

通知

入库单编号

新到料

日志

查询 填写 提交

提交

分类

提交

提交 填写

填写

填写

查询

查询

查询

息 通

判断 入库单编号

不合格 物资

信息

质检 信息

通知

出库 信息

仓库 信息

物资 编码

物 资 编 码

物资编码 仓库编码

登录 事务

图 16-2 数据流图

(1) 能充分地反映现实世界,包括实体和实体之间的联系,能满足用户对数据处理的

要求,是现实世界的一个真实模型。

(2) 易于理解,从而可以和不熟悉计算机的用户交换意见

(3) 易于更动,当现实世界改变时容易修改和扩充。

(4) 易于向各种数据模型转换

物资编码的 E-R 图如图 16-3 所示。

入库单实体 E-R 图如图 16-4 所示。

质量检验实体 E-R 图如图 16-5 所示。

Pow erBuilder9.0 基础应用与系统开发

— 428 —

编号

计量单位

存储下限

存储上限

材质

规格

名称

物资编码

类型

现有数量

图 16-3 物资编码 E-R 图

物资编码

单价

合同数量

合同编号

库管人员

入库日期

入库单编号

采购人员

总金额

入库单

实入数量

生产厂家 仓库编码

图 16-4 入库单 E-R 图

质量检验

采购人员

生产厂家

不合格数量 抽检数量

质检日期

质检人员

仓库编码

入库单编号 质检单编号

合同编号

物资编码

图 16-5 质量检验 E-R 图

第 16 章 仓储管理信息系统开发实例

— 429 —

出库单实体 E-R 图如图 16-6 所示。

出库单 库管人员

出库数量

仓库编码

出库金额 出库单价

物资编码

领用人

领用人部门

仓库编码

出库单编号

图 16-6 出库单 E-R 图

实体和实体之间的关系如图 16-7 所示。

人员

属于

机构

处理

新到料

填写

对应

1

N

1

1

N

1

出库单

入库单

N

质检单

对应

1

1

物资 增减

属于

仓库

1 NN

1

图 16-7 实体之间联系图

16.2.2 逻辑设计

根据前述需求分析和概念设计所得的分析结果,在数据库中建立入库通知单、质量检

查登记表、新到料日志登记、物资编码表、出库记录表,同时为了记录人员以及供应商的

各种信息,还需要物资编码、机构设置、人员设置、仓库设置、入库来源、出库用途、采

购合同等表。有关这些表的字段设置及其含义见表 16-1 至表 16-11。

Pow erBuilder9.0 基础应用与系统开发

— 430 —

表 16-1 物资编码表 gg_wzbm

列 名 数据类型 可否为空 是否主键 说 明 wzbm Varchar2(24) Not null Yes 物资编码 wzmc Varchar2(20) Not null 物资名称 wzgg Varchar2(20) Null 物资规格 jldw Varchar2(10) Null 计量单位 wzcz Varchar2(30) Null 物资材质 ccsx Number(10) Null 存储上限 ccxx Number(10) Null 存储下限 wzlx Varchar2(20) Null 物资类型 xysl Number(15,5) Null 现有数量

说明:物资类型为:1、原材料 2、产成品 3、半成品

表 16-2 机构设置 gg_jgsz

列 名 数据类型 可否为空 是否主键 说 明 jgbm Char(9) Not null Yes 机构编码 jgmc Varchar2(20) Null 机构名称 Jgdz Varchar2(30) Null 机构地址 szcs Varchar2(10) Not null 所在城市

表 16-3 人员设置 gg_rysz

列 名 数据类型 可否为空 是否主键 说 明 bmbm Char(9) null 部门编码 ryxm Varchar2(10) Null 人员姓名 yhm Varchar2(10) Not Null Yes 用户名 mm Varchar2(10) Not null 密码

表 16-4 仓库设置 gg_cksz

列 名 数据类型 可否为空 是否主键 说 明 ckbm Char(9) Not null Yes 仓库编码 ckmc Varchar2(20) Null 仓库名称 ckdz Varchar2(40) Null 仓库地址 bzsm Varchar2(30) Null 备注说明

表 16-5 入库来源 gg_rkly

列 名 数据类型 可否为空 是否主键 说 明 rklybh Number(2) Not null Yes 入库来源编号 rklymc Varchar(20) Null 入库来源名称

表 16-6 出库用途 gg_ckyt

列 名 数据类型 可否为空 是否主键 说 明 ckytbh Number(2) Not null Yes 出库用途编号 ckytmc Varchar(20) Null 出库用途名称

表 16-7 部门设置 gg_bmgl

列 名 数据类型 可否为空 是否主键 说 明 bmbm Char(9) Not null Yes 部门编码 bmmc Varchar2(20) Null 部门名称 bmdh Varchar2(30) Null 部门电话

第 16 章 仓储管理信息系统开发实例

— 431 —

表 16-8 入库单 rk_rkd

列 名 数据类型 可否为空 是否主键 说 明 rkdbh Char(9) Not null Yes 入库单编号 wzbm Varchar(20) Not null 物资编码 htbh Char(9) Null 合同编号 zjdbh Char(9) Null 质检单编号 rklxbh Number(2) Null 入库类型 ckbm Char(9) Null 仓库编号 kgry Varchar2(10) Null 库管人员 rkrq Date Null 入库日期 htsl Number(15,5) Null 合同数量 sjrksl Number(15,5) Null 实际入库数量 dj Number(15,5) Null 单价 zje Number(15,5) Null 总金额 cgry Varchar2(10) Null 采购人员 sccj Varchar2(20) Null 生产厂家 bz Number(2) Null 标志

表 16-9 新到料日志 rk_xdl

列 名 数据类型 可否为空 是否主键 说 明 xdlbh Char(9) Not null Yes 新到料编号 wzbm Varchar(20) Not null 物资编码 htbh Char(9) Null 合同编号 dhsl Number(15,5) null 到货数量 ydh Char(9) Null 运单号 kgry Varchar2(10) Null 库管人员 dlrq Date Null 到料日期 wgqk Varchar2(10) Null 外观情况 cgry Varchar2(10) Null 采购人员 sccj Varchar2(20) Null 生产厂家

表 16-10 质量检验 rk_zljy

列 名 数据类型 可否为空 是否主键 说 明 zjdbh Char(9) Not null Yes 质检单编号 xdlbh Char(9) Null 新到料编号 wzbm Varchar(20) Not null 物资编码 htbh Char(9) Null 合同编号 zjry Varchar2(10) Null 质检人员 zjrq Date Null 质检日期 cjsl Number(15,5) Null 抽检数量 bhgsl Number(15,5) Null 不合格数量 cgry Varchar2(10) Null 采购人员 sccj Varchar2(20) Null 生产厂家 bz Number(2) Null 标志

表 16-11 出库单 ck_ckd

列 名 数据类型 可否为空 是否主键 说 明 ckdbh Char(9) Not null Yes 出库单编号 wzbm Varchar(20) Not null 物资编码 cklxbh Number(2) Null 出库类型 ckbm Char(9) Null 仓库编号 kgry Varchar2(10) Null 库管人员 ckrq Date Null 出库日期 cksl Number(15,5) Null 出库数量 ckdj Number(15,5) Null 出库单价

Pow erBuilder9.0 基础应用与系统开发

— 432 —

(续表)

列 名 数据类型 可否为空 是否主键 说 明 ckje Number(15,5) Null 出库金额 cksh Varchar2(10) Null 审核人员 lyr Varchar2(10) Null 领用人 lybwbm Char(9) Null 领用单位编码 bz Number(2) Null 审核标志

16.2.3 物理设计

定义表的完整性约束是一件非常重要的工作。在设计数据库时,不仅要设计数据库的

逻辑结构和物理结构,而且还要仔细考虑完整性约束定义,在建表时一并解决。每一个关

系都应该有一个主键,PRIMARY KEY 就是用来定义主键的,该约束可以保证主键的值

在关系中惟一且不空。

下面通过建表语句给出本系统的约束关系。用户在 Oracle 的 SQL/PLUS 中执行即

可。

(1) 机构编码表:

CREATE TABLE �CCGL�.gg_jggl ( jgbm CHAR (9) CONSTRAINT jgbm PRIMARY KEY, --机构编码 jgmc VARCHAR2 (20) CONSTRAINT jgmc NOT NULL, --机构名称 jgdz VARCHAR2 (30), --机构地址 szcs VARCHAR2 (10) --所在城市 ) TABLESPACE �USERS�; COMMIT;

(2) 部门编码表:

CREATE TABLE �CCGL�.gg_bmgl ( bmbm CHAR (9) CONSTRAINT bmbm PRIMARY KEY, --部门编码 bmmc VARCHAR2 (20) CONSTRAINT bmmc NOT NULL, --部门名称 bmdh VARCHAR2 (30) --部门电话 ) TABLESPACE �USERS�; COMMIT;

(3) 仓库设置表。

CREATE TABLE �CCGL�.gg_ckgl( ckbm CHAR(9) CONSTRAINT ckbm PRIMARY KEY,--仓库编码 ckmc VARCHAR2(20) CONSTRAINT ckmc NOT NULL,--仓库名称 ckdz VARCHAR2 (40), --仓库地址 bzsm VARCHAR2 (30) --备注说明 ) TABLESPACE �USERS�; COMMIT;

(4) 人员设置表:

CREATE TABLE �CCGL�.gg_rygl(

第 16 章 仓储管理信息系统开发实例

— 433 —

yhm VARCHAR2 (10) CONSTRAINt rybm PRIMARY KEY, --用户名

mm VARCHAR2 (10) CONSTRAINT mm NOT NULL, --密码

ryxm VARCHAR2 (10), --人员姓名

jgbm CHAR (9) --所属机构

)

TABLESPACE �USERS�; COMMIT;

(5) 物资编码表:

CREATE TABLE �CCGL�.gg_wzbm( wzbm VARCHAR2 (24) CONSTRAINT wzbm PRIMARY KEY, --物资编码 wzmc VARCHAR2 (20) CONSTRAINT wzmc NOT NULL, --物资名称 jldw VARCHAR2 (10), --单位 wzgg VARCHAR2 (30), --规格 wzcz VARCHAR2 (30), --材质 ccsx NUMBER (10,4), --存储上限 ccxx NUMBER (10,4), --存储下限 wzlx VARCHAR2 (10), --物资类型 xysl NUMBER (15,5) --现有数量 ) TABLESPACE �USERS�; COMMIT;

(6) 出库用途表:

CREATE TABLE �CCGL�.gg_ckyt( ckytbh Number(2), --出库用途编号 ckytmc VARCHAR2 (20) --出库用途名称 ) TABLESPACE �USERS�; COMMIT; (7) 入库来源表: CREATE TABLE �CCGL�.gg_rkly( rklybh Number(2), --入库来源编号 rklymc VARCHAR2 (20) --入库来源名称 ) TABLESPACE �USERS�; COMMIT;

(8) 新到料日志表:

CREATE TABLE �CCGL�.rk_xdl ( xdlbh CHAR (9) CONSTRAINT xdlbh PRIMARY KEY, wzbm VARCHAR2 (20) CONSTRAINT wzbm_xdl NOT NULL , htbh CHAR (9), dhsl NUMBER(15,5), ydh CHAR (9), kgry VARCHAR2 (10), dlrq DATE, wgqk Varchar2(10),

Pow erBuilder9.0 基础应用与系统开发

— 434 —

cgry VARCHAR2 (10),

sccj VARCHAR2 (20)

)

TABLESPACE �USERS�; COMMIT;

(9) 入库单表:

CREATE TABLE �CCGL�.rk_rkd( rkdbh CHAR(9) CONSTRAINT rkdbh PRIMARY KEY, wzbm VARCHAR2 (20) CONSTRAINT wzbm_rkd NOT NULL, zjdbh CHAR (9), htbh CHAR (9), rklxbh NUMBER (2), ckbm CHAR (9), kgry VARCHAR2(10), rkrq DATE, htsl NUMBER (15,5), sjrksl NUMBER (15,5), dj NUMBER (15,5), zje NUMBER (15,5), cgry VARCHAR2 (10), sccj VARCHAR2 (20), bz NUMBER (2) ) TABLESPACE �USERS�; COMMIT;

(10) 质量检验表:

CREATE TABLE �CCGL�.rk_zljy( zjdbh CHAR (9) CONSTRAINT zjdbh PRIMARY KEY, rkdbh CHAR (9), wzbm VARCHAR2(20) CONSTRAINT wzbm_zljy NOT NULL, htbh CHAR (9), zjry VARCHAR2 (10), zjrq DATE, cjsl NUMBER (15,5), bhgsl NUMBER (15,5), cgry VARCHAR2 (10), sccj VARCHAR2 (20), bz NUMBER (2) ) TABLESPACE �USERS�; COMMIT;

(11) 出库单表:

CREATE TABLE �CCGL�.ck_ckd( ckdbh CHAR(9) CONSTRAINT ckdbh PRIMARY KEY, wzbm VARCHAR2(20) CONSTRAINT wzbm_ckd NOT NULL,

第 16 章 仓储管理信息系统开发实例

— 435 —

cklxbh NUMBER (2),

ckbm CHAR(9),

kgry VARCHAR2(10),

ckrq DATE,

cksl NUMBER(15,5),

ckdj NUMBER (15,5),

ckje NUMBER (15,5),

cksh VARCHAR2 (10),

lyr VARCHAR2 (10),

lybm CHAR (9),

bz NUMBER(2)

)

TABLESPACE �USERS�; COMMIT;

16.3 建立应用程序

16.3.1 创建应用程序对象

数据库创建完成后,下面开始创建应用程序部分。

在 PowerBuilder 9.0 中,继续沿用 Workspace(工作区)和 Target(目标)的概念。所以要

先建立一个工作区,然后再建立应用程序对象及其所在的库和目标。建立工作区和应用程

序对象的方法如前所述。

在本例中,建立一个工作区命名为 work_ccgl.pbw,并假设路径为 E:\pbdata。在此工

作区下,创建应用程序对象 app_ccgl,PowerBuilder 自动在 Library 编辑框中输入

E:\pbdata\app_ccgl.pbl,在 Target 编辑框中输入 E:\pbdata\app_ccgl.pbt,确认即可。

16.3.2 创建主菜单

现在创建主菜单。选择 File|New 菜单或单击工具栏上的 New 图标,便进入新建画

板,单击 PB Object 标签页,在该页中,选择 Menu,单击 OK 按钮打开 Menu Painter,在

Menu Painter 中创建如图所示的 MDI 菜单,命名为 m_main 并锁定(设置 Lock Name 属

性)。其他属性采用默认值即可。主菜单的设置效果如图 16-8 所示。

16.3.3 主窗口

1. 创建主窗口

单击工具栏上的 New 图标,选择 PB Object 标签页,在该页中,选择 Window,单击

OK 按钮打开 Window Printer(窗口画板),新建一个窗口 w_main,并将其与 m_main 相

连。具体设置见表 16-12。

Pow erBuilder9.0 基础应用与系统开发

— 436 —

图 16-8 主菜单创建结果

表 16-12 主窗口 w_main 的属性设置表

属 性 属性取值 Title 仓储管理系统 MenuName m_main WindowType Mdi! BackColor Cream MDIClient Color Silver WindowSate Normal! X 400 Y 200 Width 3000 Height 2000

2. 为主窗口 w_main 添加脚本

现在,在窗口 w_main 的 open 事件中添加连接数据库的脚本,当程序开始运行,打

开窗口 w_main 时就可以实现应用程序与数据库的连接,具体脚本如下:

//定义连接参数

SQLCA.DBMS = "ODBC"

SQLCA.DATABASE="SCBDB"

SQLCA.AutoCommit = False

SQLCA.DBParm = "ConnectString=’DSN=pbForScmdb;UID=ccgl;PWD=ccgl’"

Connect using SQLCA;

//错误处理

if SQLCA.sqlcode<>0 then

MessageBox("连接错误",SQLCA.sqlerrtext,stopsign!)

halt

第 16 章 仓储管理信息系统开发实例

— 437 —

end if

上述脚本的作用是设置默认事务对象 SQLCA 的连接属性,意思是使用“ODBC�接口

连接数据库,连接参数 DSN,UID,PWD 分别赋值数据源名称,用户和密码。在

CloseQuery 事件中输入如下的程序代码。在用户通过关闭主程序窗口的方式来关闭应用

程序时,将弹出一个对话框,询问是否退出系统:

if MessageBox("退出系统","您是否确认退出系统?",Question!,YesNo!)=2 then

return 1

else

return 0

end if

3. 完成应用程序的雏形

打开 Application Object(应用对象)app_ccgl,在应用对象画板中 app_ccgl 对象的 open事件中添加脚本:

open(w_main) //当应用程序运行时,即打开主窗口

完成这一切以后,保存所有对象,准备运行应用程序。单击 PowerBuilder 工具栏上

的运行图标,将看到如图 16-9 所示的结果。

图 16-9 系统运行界面

16.3.4 需要创建的对象

下面说明系统需要创建的全部对象。 (1) 系统的全部窗口说明及其继承关系见表 16-13。

表 16-13 示例程序所要创建的窗口一览表

类 别 窗口名 说 明 公共部分 w_main 系统主窗口

w_modfather 维护父窗口(继承自 w_main) w_quefather 查询父窗口(继承自 w_main) 父窗口 w_busfather 业务父窗口(继承自 w_main) w_wzbm 物资编码(继承自 w_modfather) w_jggl 机构管理(继承自 w_modfather) w_bmgl 部门管理(继承自 w_modfather) w_rygl 人员管理(继承自 w_modfather) w_ckgl 仓库管理(继承自 w_modfather) w_rkly 入库来源(继承自 w_modfather)

系统设置

w_ckyt 出库用途(继承自 w_modfather)

Pow erBuilder9.0 基础应用与系统开发

— 438 —

(续表)

类 别 窗口名 说 明 w_rkd 入库单(继承自 w_busfather) w_xdl 新到料日志(继承自 w_busfather) 入库管理 w_zjys 质检验收(继承自 w_busfather) w_ckd 出库单(继承自 w_busfather)

出库管理 w_cksh 出库审核(继承自 w_busfather) w_rkdcx 入库单查询(继承自 w_quefather) w_ckdcx 出库单查询(继承自 w_quefather) w_ccqc 超储缺储(继承自 w_quefather)

库存管理

w_kchz 库存汇总(继承自 w_quefather)

(2) 系统的全部数据窗口见表 16-14。

表 16-14 示例程序所要创建的数据窗口一览表

类 别 数据窗口对象名 说 明 d_wzbm 连接到 w_wzbm,进行物资编码的维护 d_jggl 连接到 w_jggl,进行机构管理的维护 d_bmgl 连接到 w_bmg,进行部门管理的维护 d_rygl 连接到 w_rygl,进行人员管理的维护 d_ckgl 连接到 w_bkgl,进行仓库管理的维护 d_rkly 连接到 w_rkly,进行入库来源的维护

系统设置

d_ckyt 连接到 w_bkyt,进行出库用途的维护 d_xdl 连接到 w_xdl,进行新到料日志的维护 d_rkd 连接到 w_rkd,进行入库单的维护 入库管理

d_zjys 连接到 w_zjys,进行质量检验的维护 d_ckd 连接到 w_ckd,进行出库单的维护

出库管理 d_cksh 连接到 w_cksh,进行出库审核的维护 d_ccqc 连接到 w_ccqc,进行超储缺储的维护 d_kchz 连接到 w_kchz,进行库存汇总的查询 d_rkdcx 连接到 w_rkdcx,进行入库单的查询

库存管理

d_ckdcx 连接到 w_ckdcx,进行出库单的查询

(3) 系统的菜单见表 16-15。

表 16-15 示例程序所要创建的菜单一览表

类 别 菜单名称 说 明 m_1 系统设置子菜单 m_2 入库管理子菜单 m_3 出库管理子菜单 m_4 库存管理子菜单

系统主菜单

m_5 系统维护子菜单 m_1.m_11 物资编码子菜单,与 w_wzbm 相连 m_1.m_12 机构管理子菜单,与 w_jggl 相连 m_1.m_13 部门管理子菜单,与 w_bmgl 相连 m_1.m_14 人员管理子菜单,与 w_rygl 相连 m_1.m_15 仓库管理子菜单,与 w_ckgl 相连 m_1.m_16 入库来源子菜单,与 w_rkly 相连

系统设置子菜单

m_1.m_11 出库用途子菜单,与 w_ckyt 相连 m_2.m_21 新到料子菜单,与 w_xdl 相连 m_2.m_22 入库单子菜单,与 w_rkd 相连 入库管理子菜单 m_2.m_23 质量检验子菜单,与 w_zjys 相连 m_3.m_31 出库单子菜单,与 w_ckd 相连

出库管理子菜单 m_3.m_32 出库单审核子菜单,与 w_ckd 相连

第 16 章 仓储管理信息系统开发实例

— 439 —

(续表)

类 别 菜单名称 说 明 m_4.m_41 入库单查询子菜单,与 w_rkdcx 相连 m_4.m_42 出库单查询子菜单,与 w_ckdcx 相连 m_4.m_43 超储缺储子菜单,与 w_ccqc 相连 m_4.m_44 库存汇总子菜单,与 w_kchz 相连

库存管理子菜单

m_4.m_45 数据备份子菜单,与 w_sjbf 相连 m_5.m_51 更改口令子菜单,与 w_ggyh 相连

数据维护 m_5.m_52 更改用户子菜单,与 w_ggyh 相连

16.4 系统设置模块设计

16.4.1 创建父窗口对象

在本示例系统中,由于考虑到要多处用到类似的窗口和数据窗口,所以采用先创建一

个祖先窗口,以后其他窗口都从祖先窗口继承的办法来创建各个子窗口。

创建祖先窗口 w_modfather,第 1 步方法与创建主 MDI 窗口方法一样,只是在设置

属性时有较大的不同,只需将其属性 WindowType 设置为“main!”,其余属性全部采用默

认值即可。创建祖先窗口的第 2 步是在窗口中添加一个数据窗口控件 dw_1,这样从祖先

窗口 w_modfather 继承下来的所有子窗体都会自动包含一个数据窗口控件 dw_1。第 3 步

是在窗口的右下方放置 4 个控件按钮,其 Click 事件中分别用来实现数据窗口控件中数据

的增加、删除、保存以及窗口关闭的功能。查询父窗口 w_quefather 和业务父窗口

w_busfather 的创建办法与维护父窗口 w_modfater 的创建方法类似。在此不再赘述。

整个窗口界面如图 16-10 所示。

图 16-10 父窗口 w_modfather 效果

Pow erBuilder9.0 基础应用与系统开发

— 440 —

16.4.2 为 w_modfather 父窗口添加用户自定义事件及其脚本

在作其他工作之前,首先为父窗口 w_modfather 的 open 事件添加如下的脚本:

dw_1.settransobject(sqlca)

这一句脚本的作用是将数据窗口控件 dw_1 与 Powerbuilder 默认的全局事务对象

SQLCA 关联起来,这样就可以直接使用数据库操纵语句操纵数据库了。

在 cb_1 的 Click 事件中输入下面的程序代码:

//在 dw_1 的最后一行插入一行

g_currownum=dw_1.InsertRow(0)

//dw_1 获得焦点

dw_1.SetFocus()

//滚动到当前行

dw_1.ScrollToRow(g_currownum)

//设置焦点为此行的第 1 列

dw_1.SetColumn(1)

在 cb_2 的 Click 事件中输入下面的程序代码:

//得到当前记录号

g_currownum=dw_1.getrow()

//删除当前记录

deleterow(dw_1,g_currownum)

在 cb_3 的 Click 事件中输入下面的程序代码:

if update(dw_1,true,false)=1 then

//保存修改成功,提交修改

dw_1.resetupdate()

commit using SQLCA;

MessageBox(�成功�,�保存成功!�) else //保存修改失败,取消所做的修改

rollback; //弹出一个对话框警告

messagebox("保存错误!!!","数据保存失败") end if

在 cb_4 的 Click 事件中输入下面的程序代码:

//关闭父窗口 close(parent)

16.4.3 创建子窗口对象

对应系统设置菜单,创建 7 个数据维护子窗口:w_wzbm(物资编码)、w_jggl(机构管

理)、w_rygl(人员管理)、w_bmgl(部门管理)、w_rkly(入库来源)、w_ckyt(出库用途)、

w_ckgl(仓库设置),它们的创建都是通过继承 w_modfather 实现的。方法是选择主菜单的

第 16 章 仓储管理信息系统开发实例

— 441 —

File>Inherit 或直接单击工具栏的继承按钮,然后在弹出的窗口中选择 w_modfather 窗口即

可。各子窗口中分别包含一个数据窗口控件 dw_1,同时继承 4 个按钮,分别实现增加、

删除、保存和关闭的功能。下面以 w_wzbm(物资编码)为例,介绍如何实现相关窗口的创

建。物资编码窗口 w_wzbm 的属性设置见表 16-16。

表 16-16 物资编码 w_wzbm 窗口的属性设置表

属 性 属性取值 Title 物资编码 WindowType Main! BackColor Cream WindowSate Normal! X 200 Y 200 Width 2900 Height 1800

设置 dw_1 数据窗口控件的属性见表 16-17。

表 16-17 窗口 w_wzbm 的 dw_1 数据窗口控件的属性设置表

属 性 属性取值 Title 物资编码 DataObject d_wzbm X 100 Y 100 Width 2500 Height 1400

16.4.4 创建数据窗口对象

根据系统需要,创建 7 个数据窗口对象:d_wzbm、d_jggl、d_rygl、d_bmgl、

d_ckgl、d_rkly、d_ckyt。有关数据窗口的说明见表。在此处只列出这几个数据窗口对象

的设置要求见表 16-18。

表 16-18 数据窗口的属性设置表

属 性 属性取值 Alignment Center(2) FaceName 宋体 Size 10 TextColor Bule

下面以 d_rygl 数据窗口为例,介绍如何创建数据窗口对象。

d_rygl 的显示风格用 Grid 类型,数据源为 Quick Select,选择表 gg_wzbm 的全部字

段,显示标题设置为如图 16-11 所示。

需要说明的是,对 d_rygl 的 jgbm 应该设置为下拉式的,即当单击这一数据列时,弹

出一个下拉列表。具体设置如图 16-12 所示。

即把数据列“jgbm”的数据源设为数据窗口“d_bmgl”,该数据列的显示值为“部门

名称”,实际值为“部门编码”。

Pow erBuilder9.0 基础应用与系统开发

— 442 —

图 16-11 数据窗口 d_rygl 的设置效果 图 16-12 数据窗口 d_rygl 的 jgbm 的属性设置

16.4.5 为系统主菜单 m_main 添加脚本

在创建了各个窗口对象之后,现在打开系统主菜单 m_main,为系统主菜单 m_main

系统设置子菜单添加以下的各个子菜单脚本。

(1) m_1.m_11(物资编码),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_wzbm,w_main,3,original!)

(2) m_1.m_12(机构管理),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_jggl,w_main,3,original!)

(3) m_1.m_13(部门管理),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_bmgl,w_main,3,original!)

(4) m_1.m_14(人员管理),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_rygl,w_main,3,original!)

(5) m_1.m_15(仓库管理),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_ckgl,w_main,3,original!)

(6) m_1.m_16(入库来源),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_rkly,w_main,3,original!)

第 16 章 仓储管理信息系统开发实例

— 443 —

(7) m_1.m_17(出库用途),为该菜单项的 Clicked 事件添加如下脚本:

opensheet(w_ckyt,w_main,3,original!)

至此,已完成开发系统设置模块的所有工作。

16.5 入库管理

16.5.1 入库管理模块设计

1. 创建窗口对象

入库管理模块主要由新到料日志、质检验收、填写入库单 3 个子模块组成,完成入库

管理的货物新到登记,然后由质检人员对货物的外观情况、破损情况进行检验,合格之后

由库管人员填写入库单。在这个模块的设计中仍然采用模块复用的思想。首先,创建

w_busfather 父窗口,并在其上放置控件 dw_1、Static Text、DropDownListbox 和 4 个

Command Button,各控件的属性设置见表 16-19。

表 16-19 窗口 w_busfather 的属性设置表

控 件 属 性 属性取值 DataObject 为空 HscrollBar 选中 VscrollBar 选中 width 2725

dw_1

Height 1050 FacaName 宋体 ddlb_1 Size 10

st_1 Text 请选择 cb_1 Text 增加 cb_2 Text 删除 cb_3 Text 保存 cb_4 Text 退出

创建好父窗口 w_busfather 之后,在 cb_1 的 Clicked 事件中添加如系统设置模块中有

关 w_modfather 其上的 cb_1 代码,其他 cb_2、cb_3、cb_4 类似。在所有继承 w_busfather

的子模块中都会具有增加、删除、保存、退出的功能,而不必重复书写代码。 继承 w_busfather 分别创建 w_xdl(新到料日志)、w_zjys(质检验收)、w_rkd(入库单)3

个窗口对象,然后在主窗口入库管理的相应菜单事件中添加打开其页面的脚本如下:

opensheet(w_xdl,w_main,3,original!)

opensheet(w_zjys,w_main,3,original!)

opensheet(w_rkd,w_main,3,original!)

2. 创建数据窗口对象

创建 3 个数据窗口对象 d_xdl、d_rkd、d_zjys,风格采用 Grid 型,数据源为

QuickSelect,分别选择库表 rk_xdl、rk_zjys、rk_rkd 的全部字段,并与 w_xdl(新到料日

志)、w_zjys(质检验收)、w_rkd(入库单)的 DataObject 相关联。以 d_rkd 为例,设置属性

Pow erBuilder9.0 基础应用与系统开发

— 444 —

FaceName 为宋体,Size 为 10 号,TextColor 为 Blue(蓝色)。各字段的属性见表 16-20。

表 16-20 数据窗口 d_rkd 中的字段属性设置

Name StyleType DataWindow Display Coloum Data Coloum wzbm DropDownDW d_wzbm wzmc wzbm kgry DropDownDW d_rygl yhm yhm cgry DropDownDW d_rygl yhm yhm sccj DropDownDW d_jggl jgmc jgbm

数据窗口 d_xdl 的显示标题如图 16-13 所示。

图 16-13 数据窗口 d_xdl 的设置效果

16.5.2 新到料日志

新到料日志主要完成对新到产品的登记。由专门的库管人员负责登记每项到料产品的

基本情况,以备后续的质检签发功能调用相关数据。主界面如图 16-14 所示。

图 16-14 新到料日志运行效果

在新到料日志窗口的 Open 事件中加入如下的程序代码:

dw_1.retrieve()

在 cb_1 的 Clicked 事件中加入如下脚本,每次单击“增加”按钮时自动将到料日期

设置为当前系统日期:

第 16 章 仓储管理信息系统开发实例

— 445 —

date now_date

//取得系统日期

now_date=today()

//自动设置到料日期为系统时间

dw_1.setitem(g_currownum,"dlrq",now_date)

16.5.3 质检签发

质检签发的数据取自新到料日志表,进入此模块之后首先选择已经到料的物资编号,

选择之后,自动将符合的物资基本信息如物资编码、新到料编号、采购人员、生产厂家、

合同编号等公共信息填入质量检验的录入界面,如图 16-15 所示。

图 16-15 质检签发的运行效果

在质检验收(w_zjys)的 Open 事件中加入如下代码,用于选择新到料编号:

dw_1.retrieve()

//获取新到料编号

declare xdlcur cursor for

select xdlbh from ccgl.rk_xdl using sqlca;

open xdlcur;

if sqlca.sqlcode=0 then

string bh

fetch xdlcur into :bh;

do while sqlca.sqlcode=0 and sqlca.sqlnrows>0

ddlb_1.additem(bh)

fetch xdlcur into :bh ;

loop

close xdlcur ;

end if

在 ddlb_1 的 Selectionchanged 事件中加入如下代码,用于将符合条件物资的相关信息

导入质检验收模块:

Pow erBuilder9.0 基础应用与系统开发

— 446 —

string bh,wzbm,cgry,sccj,htbh

date now_date

integer rownum

bh=ddlb_1.text

//获取质检单的相关信息

select wzbm,cgry,sccj,htbh into :wzbm,:cgry,:sccj,:htbh

from ccgl.rk_xdl

where xdlbh=:bh using sqlca;

//在 dw_1 的最后一行插入一行

rownum=dw_1.InsertRow(0)

//dw_1 获得焦点

dw_1.SetFocus()

//滚动到当前行

dw_1.ScrollToRow(rownum)

//设置焦点为此行的第 1 列

dw_1.SetColumn(1)

//显示相关信息

dw_1.object.wzbm[rownum]=wzbm

dw_1.object.cgry[rownum]=cgry

dw_1.object.sccj[rownum]=sccj

dw_1.object.htbh[rownum]=htbh

dw_1.object.xdlbh[rownum]=bh

//取得系统日期

now_date=today()

//自动设置到料日期为系统时间

dw_1.setitem(rownum,"zjrq",now_date)

16.5.4 入库单

在完成新到日志的填写,并经过质量检验,外观情况和到货数量都统计出来之后,要

进行的是入库单的正式录入,并同时更改物资的现有数量。运行效果如图 16-16 所示。

图 16-16 入库单的运行效果

第 16 章 仓储管理信息系统开发实例

— 447 —

在 w_rkd(入库单)的 Open 事件中加入选择质检单编号的代码:

w_1.retrieve()

//获取质检单编号

declare zjdcur cursor for

select zjdbh from ccgl.rk_zljy using sqlca;

open zjdcur;

if sqlca.sqlcode=0 then

string bh

fetch zjdcur into :bh;

do while sqlca.sqlcode=0 and sqlca.sqlnrows>0

ddlb_1.additem(bh)

fetch zjdcur into :bh ;

loop

close zjdcur ;

end if

在 w_rkd(入库单)cb_3 的 Clicked 事件中加入更新库存数量的代码:

double num,in_num

string wzbm

if update(dw_1,true,false)=1 then

//保存入库数据到 rk_rkd 中

commit using sqlca;

//取得物资编码和入库数量

wzbm=dw_1.getitemstring(dw_1.getrow(),"wzbm")

in_num=dw_1.getitemnumber(dw_1.getrow(),"sjrksl")

//取得现有库存

select xysl into :num from ccgl.gg_wzbm where wzbm=:wzbm using sqlca;

//将入库数量相加

num=in_num+num;

//更新现有库存

update ccgl.gg_wzbm set xysl=:num where wzbm=:wzbm using sqlca;

if sqlca.sqlcode>=0 then

commit using sqlca;

messagebox("成功","更新成功!")

else

rollback using sqlca;

messagebox("失败","更新失败!")

end if

end if

在 w_rkd(入库单)ddlb_1 的 Clicked 事件中加入选取相符合的物资信息的代码:

string bh,wzbm,cgry,sccj,htbh

date now_date

integer rownum

bh=ddlb_1.text

//获取质检单的相关信息

select wzbm,cgry,sccj,htbh into :wzbm,:cgry,:sccj,:htbh

from ccgl.rk_zljy

Pow erBuilder9.0 基础应用与系统开发

— 448 —

where zjdbh=:bh using sqlca;

//在 dw_1 的最后一行插入一行

rownum=dw_1.InsertRow(0)

//dw_1 获得焦点

dw_1.SetFocus()

//滚动到当前行

dw_1.ScrollToRow(rownum)

//设置焦点为此行的第 1 列

dw_1.SetColumn(1)

//显示相关信息

dw_1.object.wzbm[rownum]=wzbm

dw_1.object.cgry[rownum]=cgry

dw_1.object.sccj[rownum]=sccj

dw_1.object.htbh[rownum]=htbh

dw_1.object.zjdbh[rownum]=bh

//取得系统日期

now_date=today()

//自动设置到料日期为系统时间

dw_1.setitem(rownum,"rkrq",now_date)

至此,仓储管理系统的入库管理功能已完整实现。

16.6 出库管理

16.6.1 创建窗口对象和数据窗口

首先创建主窗口 w_ckd,这个窗口继承自入库管理中创建的父窗口 w_busfather,并

设置 ddlb_1 的 Visible 为 false,即不显示这个控件,其他属性与入库管理中的 w_rkd 设置

相同。

数据窗口控件 d_ckd 的显示风格采用 Grid,选择 QuickSelect 数据源,选择表 ck_ckd

的全部字段,并与 w_ckd 的 DataObject 相关联,显示标题设置如图 16-17 所示。

图 16-17 数据窗口 d_ckd 的设置效果

数据窗口 d_cksh 的显示风格采用 Grid,选择 QuickSelect 数据源,选择表 ck_ckd 的

ckdbh、wzbm、cklxbh、cksl、ckdj、ckje、cksh、lybm 字段,即只选择领导审核关心的相

关字段,显示标题设置如图 16-18 所示。

第 16 章 仓储管理信息系统开发实例

— 449 —

图 16-18 数据窗口 d_cksh 的设置效果

为主菜单 m_main 的出库管理子菜单添加下面的代码:

opensheet(w_ckd,w_main,3,original!)

接着创建 w_cksh(出库审核)窗口,可直接继承 w_busfather 并添加,所需控件及其属

性见表 16-21。

表 16-21 窗口 w_cksh 的属性设置表

控 件 属 性 属性取值 DataObject d_ckd HscrollBar 选中 VscrollBar 选中 width 2725

dw_1

Height 532 DataObject d_cksh HscrollBar 选中 VscrollBar 选中 width 2725

dw2

Height 532 st_1 Text 需要出库审核的单据 st_2 Text 请选择需审核的单据 ddlb_1 Visible false cb_1 Visible false cb_2 Visible false cb_3 Visible false cb_5 Visible true cb_5 Text 增加

Visible true FaceName 宋体 Size 10

Ddlb_2

TextColor Blue

16.6.2 出库单填写

出库单的界面和代码与入库管理之新到料日志部分相近,请读者自行参考。

16.6.3 出库单审核

在 w_cksh 的 GlobalVariables 设置全局变量如下:

//当前行的行号

int g_currownum,rownum

string bh

在 w_ckd 的 Open 事件中添加如下代码,作用是将出库单中 bz 设置为 0 的所有未审

Pow erBuilder9.0 基础应用与系统开发

— 450 —

核的出库单列出:

string ckdbh,wzbm, cksh,lybm

double cksl,ckdj,ckje

int cklxbh, rownum

//获取出库单信息

declare ckdcur cursor for

select ckdbh,wzbm,cklxbh,cksl,ckdj,ckje,lybm from ccgl.ck_ckd

where bz=0 using sqlca;

open ckdcur;

if sqlca.sqlcode>=0 then

fetch ckdcur into :ckdbh,:wzbm,:cklxbh,:cksl,:ckdj,:ckje,:lybm;

do while sqlca.sqlcode>=0 and sqlca.sqlnrows>0

ddlb_2.additem(ckdbh)

rownum=dw_1.InsertRow(0)

//dw_1 获得焦点

dw_1.SetFocus()

//滚动到当前行

dw_1.ScrollToRow(rownum)

//设置焦点为此行的第 1 列

dw_1.SetColumn(1)

//显示相关信息

dw_1.object.wzbm[rownum]=wzbm

dw_1.object.ckdbh[rownum]=ckdbh

dw_1.object.cklxbh[rownum]=cklxbh

dw_1.object.cksl[rownum]=cksl

dw_1.object.ckdj[rownum]=ckdj

dw_1.object.ckje[rownum]=ckje

dw_1.object.lybm[rownum]=lybm

fetch ckdcur into :ckdbh,:wzbm,:cklxbh,:cksl,:ckdj,:ckje,:lybm ;

loop

close ckdcur ;

end if

在 ddlb_2 的 selectionchanged 事件中添加如下代码,作用是将符合条件的出库单列

出:

string wzbm, cksh,lybm

double cksl,ckdj,ckje

int cklxbh

bh=ddlb_2.text

//获取质检单的相关信息

select wzbm,cklxbh,cksl,ckdj,ckje,lybm

into :wzbm,:cklxbh,:cksl,:ckdj,:ckje,:lybm

from ccgl.ck_ckd

where ckdbh=:bh using sqlca;

//在 dw_1 的最后一行插入一行

rownum=dw_2.InsertRow(0)

//dw_1 获得焦点

dw_2.SetFocus()

第 16 章 仓储管理信息系统开发实例

— 451 —

//滚动到当前行

dw_2.ScrollToRow(rownum)

//设置焦点为此行的第 1 列

dw_2.SetColumn(1)

//显示相关信息

dw_2.object.wzbm[rownum]=wzbm

dw_2.object.ckdbh[rownum]=bh

dw_2.object.cklxbh[rownum]=cklxbh

dw_2.object.cksl[rownum]=cksl

dw_2.object.ckdj[rownum]=ckdj

dw_2.object.ckje[rownum]=ckje

dw_2.object.lybm[rownum]=lybm

在 cb_5 的 Clicked 中添加如下代码,即更新所选择的行的审核人:

string cksh

cksh=dw_2.object.cksh[rownum]

update ccgl.ck_ckd set cksh=:cksh where ckdbh=:bh using sqlca;

16.7 库存管理

库存管理重要性的另一方面在于其能提供比较完整的库存状况查询,方便管理者对库

存情况的了解,从而有利于制定相应的决策。在本节中,主要以入库出库情况的查询和超

储缺储分析为主,为读者开发相关的系统提供参考。

16.7.1 库存查询

鉴于前面的章节中已经对如何创建窗口对象和数据窗口对象讲述得非常详细,在此只

给出程序实现的界面如图 16-19 所示。

图 16-19 库存查询的运行效果

Pow erBuilder9.0 基础应用与系统开发

— 452 —

设计思想是首先选择入库查询还是出库查询,再选择根据什么样的条件进行查询。可

以按物资编码查询、按入库时间查询、按采购人员查询、按供应单位查询,这些查询条件

的构建主要是为了从不同的角度反映库存的状况。在 RadioButton 的 Clicked 事件中添加

根 据 不 同 条 件 选 择 不 同 的 查 询 编 码 至 ddlb_1 的 items 选 项 中 , 并 由 ddlb_1 的

selectionchanged 事件触发在 dw_1 中显示符合的数据。具体的代码读者可按照前面入库管

理中的代码推理而得,在此不再赘述。

16.7.2 统计报表

下面以超储缺储子功能的实现为例,讲述统计报表功能的分析与实现。超储、缺储主

要记录了每种物资现有数量与合理库存数量对比的结果,可以起到库存警报的作用。主界

面如图 16-20 所示。

图 16-20 超储缺储分析的运行效果

首先创建 w_ccqc 窗口,读者可自行创建。其次创建数据窗口 d_ccqc,显示风格采用

Grid,选择 QuickSelect 数据源,选择表 gg_wzbm 的 wzbm、wzmc、jldw、wzgg、wzcz、

ccsx、xysl 字段,显示标题如图 16-21 所示。

图 16-21 数据窗口 d_ccqc 的设置效果

需要指出的是,与前述示例中的不同之处为超储、缺储数量是 ComputedFiled 类型,

第 16 章 仓储管理信息系统开发实例

— 453 —

具 体 做 法 是 : 选 择 Insert|Control|ComputedFiled , 插 入 在 合 适 的 位 置 , 并 在 弹 出 的

Expression 中填入:xysl-ccsx,意为用物资编码表中的现有数量减去存储上限即得到超储

数量,并同时在标题栏中插入一个 Text 类型,其 Text 属性为“超储数量”。

在 rb_1 的 Clicked 事件中加入如下代码:

string wzmc,wzbm,jldw,wzgg,wzcz

double ccsx,xysl

int row_number

//获取出库单信息

declare wzcur cursor for

select wzmc,wzbm,jldw,wzgg,wzcz,ccsx,xysl from ccgl.gg_wzbm using

sqlca;

open wzcur;

if sqlca.sqlcode>=0 then

fetch wzcur into :wzmc,:wzbm,:jldw,:wzgg,:wzcz,:ccsx,:xysl;

do while sqlca.sqlcode>=0 and sqlca.sqlnrows>0

row_number=dw_1.InsertRow(0)

//dw_1 获得焦点

dw_1.SetFocus()

//滚动到当前行

dw_1.ScrollToRow(row_number)

//设置焦点为此行的第 1 列

dw_1.SetColumn(1)

//显示相关信息

dw_1.object.wzbm[row_number]=wzbm

dw_1.object.wzmc[row_number]=wzmc

dw_1.object.jldw[row_number]=jldw

dw_1.object.wzgg[row_number]=wzgg

dw_1.object.wzcz[row_number]=wzcz

dw_1.object.ccsx[row_number]=ccsx

dw_1.object.xysl[row_number]=xysl

fetch wzcur into :wzmc,:wzbm,:jldw,:wzgg,:wzcz,:ccsx,:xysl ;

loop

close wzcur ;

end if

执行程序选择超储分析 RadioButton,即可看到如图 16-20 所示的效果。

16.8 小结

至此,一个完整的仓储管理系统就创建完毕了。在本例中简要讲述了仓储管理系统的

系统设计、数据库设计、祖先窗口的创建、系统设置模块、入库管理模块、出库管理模

块、库存管理模块、系统维护模块的开发,希望这个示例对读者学习 PowerBuilder 快速

开发 C/S 结构下的管理系统有所帮助。

Pow erBuilder9.0 基础应用与系统开发

— 454 —

参 考 文 献

[1] 何旭洪等.PowerBuilder 8.0 数据库系统开发实例导航.北京:人民邮电出版社,

2002.

[2] 龙腾等.PowerBuilder 8.0 实例.北京:国防科技大学出版社,2002.

[3] 段兴等.PowerBuilder 8.0 实用程序设计 100 例.北京:人民邮电出版社,2003.

[4] 崔杜武等.PowerBuilder 8.0 从基础到应用.北京:人民邮电出版社,2002.

[5] 邓甫国等.PowerBuilder 7.0 从入门到精.北京:北京航空航天大学出版社,2000.

[6] 刘红岩等.PowerBuilder 7.0 应用开发技术详解.北京:电子工业出版社,2000.

[7] 李超等.PowerBuilder 8.0 编程基础.北京:清华大学出版社,2002.

[8] 鲍永刚等.PowerBuilder 8.0 核心技术及开发实例.北京:电子工业出版社,2002.

[9] 原雪等.PowerBuilder 8 实例.北京:国防科技大学出版社,2002.

[10] 萨师煊等.数据库系统概论(第二版).北京:高等教育出版社,1991.

[11] 唐泽圣等.程序设计 Java 2..北京:电子工业出版社,2001.

[12] 邹华等.Servlet/JSP 程序设计技术与实例.北京:人民邮电出版社,2001.

[13] 柴晓路.Web 服务架构与开放互操作技术.北京:清华大学出版社,2002.

[14] Working with Web and Jsp Targets

http://sybooks.sybase.com/onlinebooks/group-pb/pbg0900e/wbtarget

[15] Simon Brown,Robert Burdick,Jayson Falkner,Professional JSP,2nd Edition,

Wrox press 2002.

[16] 李安渝等.Web Services 技术与实现.北京:国防工业出版社,2003.

[17] PowerBuilder Extension Reference. Sybase Inc

[18] PowerBuilder Native Interface Programmer�s Guide and Reference. Sybase Inc [19] Release Bulletin PowerBuilder Enterprise 9.0.1 http://manuals.sybase.com/

onlinebooks/group-pb/pbr0901e/pbentrb/@Generic__BookView;lang=zh [20] http://www.sybase.com/support/manuals/ [21] http://www.sybase.com/pb9_samples [22] 师夷工作室译.Steven Holzner.XML 完全探索 inside XML.北京:中国青年出版

社,2001. [23] http://www.techno-kitten.com/Changes_to_PowerBuilder/New_In_PowerBuilder_9/PB9New_-_

PowerBuilder_Document/pb9new_-_powerbuilder_document.html [24] 数据库处理——基础、设计与实现(第七版).北京:电子工业出版社,2001. [25] Jayson Falkner 等.JSP Web 编程指南.北京:电子工业出版社,2002. [26] 肖金秀等.JSP 程序设计教程.北京:冶金工业出版社,2003.