PowerBuilder编程技巧.pdf
PowerBuilder 编程技巧随着数据库技术在各行各业的广泛应用,作为企业级数据库前端开发工具的P owerBuilder 日益成为开发人员的得力助手。PowerBuilder 以其开放的体系结构,友好的用户界面和简洁高效的开发环境赢得了众多程序员的喜爱,连续多年被评为美国计算机界的年度风云产品,在数据库开发工具领域占据了高达44的市场份额。在这里,笔者将自己平日用PowerBuilder 作开发的一些体会整理出来,奉献给大家。PowerBuilder 是由多个功能模块组成的可视化集成开发环境,是面向对象的开发工具,用它可以方便地建立起基于Windows 的分布式数据库应用。其功能模块分别完成应用管理、窗口对象设计、菜单对象设计、数据窗对象设计和数据库查询等工作,这些功能模块由于PowerBuilder 提供的色彩丰富的工具条而被称作Painter(画板)。下文便依据各模块作大的分类介绍相应的编程技巧。一、有关应用的编程技巧仅让应用程序运行一次的技巧:有时需要限制一个PowerBuilder 应用同时运行的实例(Instance)个数或仅让应用运行一次,我们可以通过调用WindowsSDK 函数或使用PowerBuilder 的 Ha ndle()函数来实现。先谈调用SDK 函数的方法。为了调用SDK 函数,需要在ApplicationPainter 的菜单项Declar eGlobalExternalFunctions 中定义:FunctionuintGetModuleHandle(stringModuleName)LibraryKernel.exe FunctionuintGetModuleUsage(uintModuleHa ndle)LibraryKernel.exe 下面这段程序写在Application 的 Open 事件中。它先通过调用SDK 函数 GetModuleHandle()获得指定应用程序的句柄,然后调用 GetModuleUsage()函数确定应用程序同时运行的实例个数。uint IApplHandle int App_num IApplHandle=GetModuleHandle(c:remre m.exe)if IApplHandle0 then App_num=GetModuleUsage(IApplHandle)if App_num1 then Messagebox(注意,本程序已经运行!,Stopsign!)return endif endif Open(w_main)若需要限制应用同时运行的实例个数,比如仅允许同时运行N 个实例,那么将上述程序中的语句“if App_num1 then”改为“if App_numN then”即可。采用 Handle()函数的方法更简洁一些,代码如下:int hand hand=Handle(this,TRUE)If hand0 then Messagebox(注意,本程序已经运行!,Stopsign!)Halt else Open(w_main)end if 二、有关窗口的编程技巧 1、提供类似中文之星的实时帮助条中文之星2.0 版的链形菜单管理器提供了实时帮助条,增强了系统的易用性,在PowerBuilder 中也可以实现类似的功能。当鼠标移动到窗口中的某些控制(Control),如编辑器、图片等时,会在鼠标附近自动产生帮助条,实时地提示操作要领。首先在窗口 w_main 中任意位置定义一个黄底黑字的静态文本st_help,设定 st_help.visible=false,st_help.text=&Help;然后在该窗口模块的 DeclareWindowFunctions.下定义函数show_help(),其参数只有一个,参数名为 text,类型为string,通过传值方式接收参数;无返值。show_help()代码如下:if st_help.visible then return st_help.text=text st_help.width=Len(st_help.text)*38 st_help.x=w_main.PointerX()st_help.y=w_main.PointerY()+50 if st_help.x+st_help.widthw_main.Workspacewidth()then st_help.x=w_main.Workspacewidth()-st_help.width end if if st_help.y+st_help.heightw_main.Workspaceheight()then st_help.y=w_main.Workspaceheight()-st_help.height end if st_help.visible=true end if 接下来,我们就可以调用show_help()函数了。但PowerBuilder 提供的所有控制均缺乏当鼠标移至其上就触发的事件,显然,需要定义相应的用户事件。先选中准备定义用户事件的控制,如某个单行编辑器,然后在窗口模块的菜单 DeclareUserEvents.下,双击PasteEventID:中的 pbm_mousemove 条目,将其拷贝至EventID 下,取EventName 为 Mouseon,这样,我们就定义好了相应控制的用户事件Mouseon。我们可以在该控制的用户事件Mouseon 下,写下调用函数show_help()的语句:if st_help.visiblethenHide(st_help)then show_help(瞧!这便是实时帮助条!)end if 2、“跑马灯”的实现技巧有时需要用一矩形条显示少量用户特别关心的信息,这条信息串首尾相连,向一个方向循环滚动,我们通常将其称作“跑马灯”。证券业中常用“跑马灯”来显示不断变化的股票行情;实际应用中也常通过“跑马灯”来监视是否死机。我们可以写一个简单的函数running_horse()来实现“跑马灯”的显示。running_horse 有两个参数,第一个参数的参数名为textline,类型为string,传值;第二个参数的参数名为 num,类型为int,传值;函数返值类型为string。该函数的代码仅一句:returnMid(textline,(num+1)+Left(textline,num)下面就可以调用running_horse()函数了。先在一个窗口里定义好单行编辑器 sle_running_horse,在该窗口的 Open 事件下写上:sle_running_horse.text=Iamtestingrunning_horse!Timer(0.2)然后在该窗口的Timer 事件下调running_horse(),代码如下:sle_running_horse.text=running_horse(sle_running_horse.text,1)这样,当你打开这个窗口时,“跑马灯”便会运转起来。可以在程序中加些语句,适时地增减sle_running_horse.text 中的内容,你便会在“跑马灯”中看到相应变化的信息。三、有关菜单的编程技巧右键菜单的实现技巧:当你在相应的窗口或控制上按鼠标右键时,就会在鼠标所指位置弹出菜单,这就是右键菜单。程序中支持右键菜单会为用户的操作带来许多方便,同时鼠标右键可以分担部分左键的功能。在PowerBuilder 中实现右键菜单非常简单,仅两个步骤:1.设计相应菜单;2.在窗口或控制的Rbuttondown 事件下写上调用语句。先在 MenuPainter 中创建菜单rbuttonpop,rbuttonpop 有一个菜单条目(M enuitem)m_choice。然后在需要调用该菜单的窗口或控制的Rbuttondown 事件下写上:m_rbuttonpop NewMenu NewMenu=Createm_rbuttonpop NewMenu.m_choice.PopMenu(PointerX(),PointerY()至此,右键菜单制作完毕。上述语句中的NewMenu 的数据类型为m_rbuttonpop,当你在相应位置按鼠标右键时,弹出的菜单NewMenu 是菜单 m_rbuttonpop 的一个实例(Instance)。四、有关数据窗的编程技巧数据窗对象是PowerBuilder 中最重要的概念之一,它是PowerBuilder 应用区别于其它Windows 应用的重要特征,同时也是PowerBuilder 的价值所在。PowerBuilder 应用通常通过数据窗对象从数据库或其它数据源取得数据并加以显示,其数据的输入、添加、修改和删除也大都通过数据窗对象来实现。故理解并掌握数据窗概念对于用好 PowerBuilder 具有重要意义。下面给出了有关数据窗的几个编程技巧。1、自动调整大小的数据窗在 PowerBuilder 应用运行过程中,常常会用鼠标拖动窗口角以改变窗口大小,尤其是在多文档窗口(MDI)中,通常有多个sheet存在的情况下,有时为了察看后面窗口中的数据而将前面窗口缩小,但窗口缩小了,其中的数据窗并没有缩小,由此而不能方便地使用数据窗的卷滚条,那么怎样使前面窗口中的数据窗大小随窗口的大小自动调整呢?很简单,我们只需要在数据窗所在窗口的 Resize 事件下写上一句话:Resize(dw_datamon,this.Workspacewidth()-50,this.Workspaceheigh t()-50)其中 dw_datamon 是数据窗的名字,数字50 可以调整。这样,你就拥有了一个会随窗口大小变化而自动调整大小的数据窗了。卷滚条用起来很方便,不信试试。2、Retrieve 后不回卷的数据窗我们经常面对一大堆数据,其具体体现就是数据窗很长,需要拉动垂直卷滚条才能看到后面的数据,当你在包含长数据窗的窗口的Timer 事件中写下Retrieve()语句后,令人气恼的事情就会发生:Timer 事件一执行,数据窗就翻回第一页;如果Timer 事件执行的时间间歇很短,那我们就永远没有足够的时间来察看后面的数据了。下面我们着手解决这个问题。可能你已经注意到了,每个数据窗都拥有两个与R etrieve 有关的事件:Retrievestart 和 Retrieveend,它们分别允许我们在Retrieve 的前后干一些事,这正是我们所需要的。实际上,就这两个事件,我们已经能够提出两个解决方案了。其一,在Retrievestart 事件中,保存当前数据窗中可见的数据行;然后Retrieve;接着在 Retrieveend 事件中,恢复先前保存的数据行。其二,在 Retrievestart 事件中,保存当前垂直卷滚块的位置;Retrieve 后再恢复其位置。后者使用了动态数据窗函数,实现起来更简洁一些,下面详细探讨。假设你已设计好了一个在窗口w_datamon 中的数据窗dw_datamon,现在可以先定义一个保存垂直卷滚块位置的类型为string 的 Globle 变量 old_vspos,然后在该数据窗的Retriev estart 事件下输入以下语句以保存其位置:old_vspos=this.dwDescribe(DataWindow.VerticalScrollPosition)dw_datamon.SetRedraw(false)在相应的Retrieveend 事件下输入恢复垂直卷滚块位置的语句:this.dwModify(DataWindow.VerticalScrollPosition=+old_vspos)dw_datamon.SetRedraw(true)这样,数据窗上的工作已做完。下面是相应窗口上的工作。该窗口的 Open 事件下:dw_datamon.Settrans(sqlca)dw_datamon.Retrieve()timer(6)该窗口的 Timer 事件下:Setfocus(w_datamon)Retrieve(dw_datamon)至此,Retrieve 后不会回卷的数据窗dw_datamon 已经可以工作了。值得注意的是,数据窗的排序分类等操作应在 Retrieve 前就在数据库表中完成,否则Retrievestart 事件保存的卷滚块位置很可能并不是你所期待的,换句话说,Retrievestart 事件应发生在所有数据窗操作之后;另外,在每次Ret rieve 后,应将处于该数据窗上的 Focus 移开,以免具有焦点的数据窗的第一行第一列总要显示,故在窗口w_dat amon 的 Timer 事件中设置了Setfocus(w_datamon)这条语句。3、依据条件改变数据颜色依据条件改变数据颜色是许多场合都要用到的重要功能,数据颜色的改变不仅引人注目,而且能起到暗示作用,清楚地告诉用户价位的涨跌或状态的改变等。大多数证券期货实时行情显示软件都提供了这种功能。在当前价位比其前一价位高时,当前价位数据颜色变红,表示价位上涨;反之,颜色变绿,表示价位下跌;若当前价位与其前一价位相等,则数据颜色不变。PowerBuilder 没有提供解决这一问题的捷径,但我们仍可利用动态数据窗来实现。先考虑一下实现的步骤,在Retrieve 前需要把有关列的数据先保存起来;Retrieve 后我们获得了相应列的新数据;我们需要将上述二者作一比较,以确定颜色的变化。值得指出的是,由于动态数据窗函数dwModify()只能用描述数据窗的模式串作参数,不能接收变量作参数,故我们得想法把比较的结果传递给数据窗。为解决这个问题,可以在定义数据窗时多定义几个空列,这几列不与数据库表中的列相对应,它们作为存放比较结果的缓冲区。原则上若需要N 列实时地变色,则需要N 列缓冲区,就应该多定义N 个空列。下面给出了一个例子具体说明。这段程序写在某窗口的Timer 事件中,该窗口内有数据窗dw_infor,其buy、sell 列分别表示买价和卖价,需要实时地变颜色。为此,我们在数据窗dw_infor 中多定义了 buybuf 和sellbuf 两列,分别存放buy 列和 sell 列 Retrieve 前后数据比较的结果。/Red=255;Green=65280 int i,infor_rownum decimalbuy_old,sell_old,buy_new,sell_new dw_infor.SetRedraw(false)infor_rownum=dw_infor.RowCount()FOR i=1 To infor_rownum buy_oldi=dw_infor.GetitemNumber(i,buy)sell_oldi=dw_infor.GetitemNumber(i,sell)NEXT dw_infor.retrieve()FOR i=1 TO infor_rownum buy_newi=dw_infor.GetitemNumber(i,buy)sell_newi=dw_infor.GetitemNumber(i,sell)NEXT FOR i=1 TO infor_rownum dw_infor.Setitem(i,buybuf,buy_newi-buy_oldi)dw_infor.Setitem(i,sellbuf,sell_newi-sell_oldi)NEXT dw_infor.dwModify(buy.color=0tif(buybuf0,255,if(buybuf0,255,if(sellbuf0,65 280,0)dw_infor.setredraw(true)我们看到,程序在Retrieve 前后分别将 buy 和sell;列的数据写进与其类型匹配的数组中,然后将比较的结果分别写入buybuf 和 sellbuf 列,最后用函数dwModify()改变有关列的颜色。记住在该窗口的Open 事件中设置事务对象并激活Timer 事件。此外,还有一些方法可以改变颜色,比如先在某些需要变颜色的行或列设置带颜色的长方形,同时将其上面的数据窗中的数据设置成透明的,当条件改变时,可以通过改变数据窗后的长方形的颜色来实现。4、用 Enter 键替代 Tab 键切换栏目的数据窗许多情况下,PowerBuilder 应用的数据是通过数据窗输入的,而且输入的数据是单纯的数字数据,也就是说,输入内容完全可以通过敲击键盘右面的数字小键盘来完成。但在实际使用中,数据窗栏目间的切换却要通过按键盘最左边的Tab 键来实现,既不方便又影响录入速度。如果能用Enter 键替代Tab 键切换栏目就好了。由于按Enter 键是 Windows 直接支持的消息,故我们可以使用用户事件来解决问题。在用户事件中,PowerBuilder 提供的一条 pbm_事件对应Windows 的一条或几条消息。我们在数据窗dw_datamon 的用户事件中选择pbm_dwnProcessEnter 并命名为Enterkeydown。在该事件下写代码:Send(Handle(this),256,9,Long(0,0)return 1 这将把消息传递给Tab 键,同时忽略Enter 键的处理。下面是一段用数据窗接收数据的完整的程序段,其中采用了用Enter 键替代 Tab 键的代码。当光标在每行最后一列时按Enter 键,光标会移至下一行第一列;当光标在最后一行的最后一列时按Enter 键,会自动产生新行并将光标置于该行的第一列;在其它情况下按Enter 键,光标会移至当前行的下一列。这段程序仍然写在与pbm_dwnProcessEnter 相对应的用户事件Enterkeydown 下:IF This.AcceptText()0 then return 1 end if if this.getcolumn()=Long(This.DwDescribe(datawindow.column.count)then if this.getrow()=This.RowCount()then this.insertrow(0)this.scrolltorow(this.getrow()+1)this.setcolumn(1)return 1 end if end if send(handle(this),256,9,long(0,0)rerurn 1