PowerBuilder 编程技巧实例

随着数据库技术在各行各业的广泛应用,作为企业级数据库前端开发工具的Power Builder日益成为开发人员的得力助手。PowerBuilder以其开放的体系结构,友好的用户界面和简洁高效的开发环境赢得了众多程序员的喜爱,连续多年被评为美国计算机界的年度风云产品,在数据库开发工具领域占据了高达44%的市场份额。

 

          PowerBuilder进入我国的时间不长,许多编程人员希望了解并掌握这一先进工具。在这里,笔者将自己平日用PowerBuilder作开发的一些体会整理出来,奉献给大家。 PowerBuilder是由多个功能模块组成的可视化集成开发环境,是面向对象的开发工具,用它可以方便地建立起基于Windows的分布式数据库应用。其功能模块分别完成应用管理、窗口对象设计、菜单对象设计、数据窗对象设计和数据库查询等工作,这些功能模块由于PowerBuilder提供的色彩丰富的工具条而被称作"Painter"(画板)。

 

         下文便依据各模块作大的分类介绍相应的编程技巧。

一、有关应用的编程技巧仅让应用程序运行一次的技巧:

有时需要限制一个PowerBuilder应用同时运行的实例(Instance)个数或仅让应用运行一次,我们可以通过调用WindowsSDK函数或使用PowerBuilder的Handle()函数来实现。

先谈调用SDK函数的方法。

为了调用SDK函数,需要在ApplicationPainter的菜单项Declare/GlobalExternalFunctions中定义: FunctionuintGetModuleHandle(stringModuleName)Library"Kernel.exe" FunctionuintGetModuleUsage(uintModuleHandle)Library"Kernel.exe" 下面这段程序写在Application的Open事件中。

 

它先通过调用SDK函数GetModuleHandle()获得指定应用程序的句柄,然后调用GetModuleUsage()函数确定应用程序同时运行的实例个数。

uint IApplHandle int App_num IApplHandle=GetModuleHandle("ocmis.exe") if IApplHandle>0 then App_num=GetModuleUsage(IApplHandle) if App_num>1 then Messagebox("注意","本程序已经运行!",Stopsign!) return endif endif Open(w_main) 若需要限制应用同时运行的实例个数,比如仅允许同时运行N个实例,那么将上述程序中的语句 “if App_num>1 then”改为“if App_num>N then”即可。

 

 采用Handle()函数的方法更简洁一些,代码如下: int hand hand=Handle(this,TRUE) If hand>0 then Messagebox("注意","本程序已经运行!",Stopsign!) Halt else Open(w_main) endif

二、有关窗口的编程技巧

1、提供实时帮助条 中文之星2.0版的链形菜单管理器提供了实时帮助条,增强了系统的易用性,在PowerBuilder中也可以实现类似的功能。当鼠标移动到窗口中的某些控制(Control),如编辑器、图片等时,会在鼠标附近自动产生帮助条,实时地提示操作要领。

 首先在窗口w_main中任意位置定义一个黄底黑字的静态文本st_help,设定st_help.visible=false,st_help.text=&Help;

然后在该窗口模块的Declare/WindowFunctions...下定义函数show_help(),其参数只有一个,参数名为text,类型为string,通过传值方式接收参数;无返值。

show_help()代码如下: if st_help.visible then return endif 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.width>w_main.Workspacewidth() then st_help.x=w_main.Workspacewidth()-st_help.width endif if st_help.y+st_help.height>w_main.Workspaceheight() then st_help.y=w_main.Workspaceheight()-st_help.height endif st_help.visible=true

接下来,我们就可以调用show_help()函数了。但PowerBuilder提供的所有控制均缺乏当鼠标移至其上就触发的事件,显然,需要定义相应的用户事件。

先选中准备定义用户事件的控制,如某个单行编辑器,然后在窗口模块的菜单Declare/UserEvents...下,双击PasteEventID:中的pbm_mousemove条目,将其拷贝至EventID下,取EventName为Mouseon,这样,我们就定义好了相应控制的用户事件Mouseon。

我们可以在该控制的用户事件Mouseon下,写下调用函数show_help()的语句: if st_help.visible then Hide(st_help) show_help("瞧!这便是实时帮助条!") endif

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有一个菜单条目(Menuitem)m_choice。

然后在需要调用该菜单的窗口或控制的Rbuttondown事件下写上: m_rbutton popNewMenu 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.Workspaceheight()-50) 其中dw_datamon是数据窗的名字,数字50可以调整。这样,你就拥有了一个会随窗口大小变化而自动调整大小的数据窗了。卷滚条用起来很方便,不信试试。 2、Retrieve后不回卷的数据窗 我们经常面对一大堆数据,其具体体现就是数据窗很长,需要拉动垂直卷滚条才能看到后面的数据,当你在包含长数据窗的窗口的Timer事件中写下Retrieve()语句后,令人气恼的事情就会发生:Timer事件一执行,数据窗就翻回第一页;如果Timer事件执行的时间间歇很短,那我们就永远没有足够的时间来察看后面的数据了。

下面我们着手解决这个问题。可能你已经注意到了,每个数据窗都拥有两个与Retrieve有关的事件:Retrievestart和Retrieveend,它们分别允许我们在Retrieve的前后干一些事,这正是我们所需要的。

实际上,就这两个事件,我们已经能够提出两个解决方案了。

其一,在Retrievestart事件中,保存当前数据窗中可见的数据行;然后Retrieve;接着在Retrieveend事件中,恢复先前保存的数据行。

其二,在Retrievestart事件中,保存当前垂直卷滚块的位置;Retrieve后再恢复其位置。后者使用了动态数据窗函数,实现起来更简洁一些,下面详细探讨。假设你已设计好了一个在窗口w_datamon中的数据窗dw_datamon,现在可以先定义一个保存垂直卷滚块位置的类型为string的Globle变量old_vspos,然后在该数据窗的Retrievestart事件下输入以下语句以保存其位置: 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事件应发生在所有数据窗操作之后;

另外,在每次Retrieve后,应将处于该数据窗上的Focus移开,以免具有焦点的数据窗的第一行第一列总要显示,故在窗口w_datamon的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_old[i]=dw_infor.GetitemNumber(i,"buy") sell_old[i]=dw_infor.GetitemNumber(i,"sell") NEXT dw_infor.retrieve() FOR i=1 TO infor_rownum buy_new[i]=dw_infor.GetitemNumber(i,"buy") sell_new[i]=dw_infor.GetitemNumber(i,"sell") NEXT FOR i=1 TO

 infor_rownum dw_infor.Setitem(i,"buybuf",buy_new[i]-buy_old[i]) dw_infor.Setitem(i,"sellbuf",sell_new[i]-sell_old[i])

NEXT dw_infor.dwModify("buy.color='0~tif(buybuf>0,255,if(buybuf<0,65280,0))'") dw_infor.dwmodify("sell.color="0~tif(sellbuf>0,255,if(sellbuf<0,65280,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)) This.SetActionCode(1) 这将把消息传递给Tab键,同时忽略Enter键的处理。

下面是一段用数据窗接收数据的完整的程序段,其中采用了用Enter键替代Tab键的代码。当光标在每行最后一列时按Enter键,光标会移至下一行第一列;当光标在最后一行的最后一列时按Enter键,会自动产生新行并将光标置于该行的第一列;在其它情况下按Enter键,光标会移至当前行的下一列。

这段程序仍然写在与pbm_dwn ProcessEnter相对应的用户事件Enterkeydown下: IF This.AcceptText()<0 then this.setactioncode(1) return endif 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) this.setactioncode(1) return endif endif send(handle(this),256,9,long(0,0)) this.setactioncode(1)

5、数据从文本文件写入数据库表的捷径 许多情况下,文本文件中的数据排列顺序与数据库表中列的顺序一致,并且其数据类型与数据库表中对应列的一致,要做的就是将该文件中的内容存入对应数据库表中。

例如在点对点通讯的情况下,一方把数据库表中的数据以文本文件格式存储并传送给另一方,另一方要做的工作就是将收到的数据存入相同的数据库表中。

PowerBuilder有多种方式与文本文件打交道,比如使用ODBC的文本文件驱动器、采用DDE(动态数据交换)方式、采用OLE方式或使用Cursor将文本文件逐行逐列读取并写入数据库表等。

但在这种情况下,还有一种更快捷的方法:使用ImportFile()函数将文本文件内容直接倒入数据库表中。

该函数用法为: datawindowname.ImportFile(filename{,startrow{,endrow& {,startcolumn{,endcolumn{,dwstartcolumn}}}}}) ImportFile()函数要求数据窗列的数据类型和列的排列顺序必须与文本文件中的数据相匹配。

 

在存储文本文件时要注意接收数据库对表的格式要求,比如在SYBASE中,表格的列与列间是采用Tab键区分的,所以如果要用ImportFile()函数将文件内容写入SYBASE表中,就要将相应文本文件的列与列间加入Tab键——这在文件形成时即可加入。

下面的代码将文件infor.txt写入数据窗dw_infor,并将与dw_infor对应的数据库表更新: int impt if (FileLength("c:/data/infor.txt")>0) then impt=dw_infor.ImportFile("c:/data/infor.txt") if impt>0 then dw_infor.settrans(sqlca) update(dw_infor) commit; If sqlca.sqlcode=-1 then messagebox("SQLERROR",sqlca.sqlerrtext) endif else messagebox("注意","文件infor.txt写入失败!") endif endif 此外,ImportFile()函数还支持从文本文件的指定开始行列到结束行列读取并写入数据;

并且支持.dbf(dBase)文件的读写,这给原有台式数据库数据文件向新的分布式数据库表中转换带来了极大的方便。

五、有关文件输出的编程技巧

 

 1、打印前的模拟显示的编程技巧在制作完一段文档或表格后,我们在正式打印输出前总希望看一下模拟显示的效果,看看是否需要调整打印参数等。笔者在调试一个标签打印程序时就遇到了这个问题,标签文档的输出格式正确与否、标签间的横竖间距恰当与否等均可通过模拟显示直观地看到。

总之,我们需要打印前的模拟显示。单纯写一段模拟显示程序并不难,但多思考一下往往能找到好方法。在文件读写时需要有文件号、在用Socket编程时涉及到Socket号,同样,多任务的操作系统管理进程时也采用了进程号——显然,整个软件系统都是采用一些数据类型为整型的变量来管理它要处理的逻辑对象的。

一台打印机有它的逻辑文件名,在操作系统看来,它和磁盘上的一个文本文件没有两样。

这样,向打印机输出与向磁盘文件输出就统一起来了,我们就依据这个想法来写模拟显示程序。

大家知道,打印机的逻辑名是"PRN",我们可以对此作与磁盘文件相同的处理。

下面的程序可写于按钮cb_print的Click事件下:

int retn,fileno

string filename,write_string

 retn=Messagebox("提示","输出至打印机选,~r~n模拟显示(c:/simulate.txt)选,~r~n取消选! " ,Information!,YesNoCancel!,1)

 Choose

Case retn Case 1 sle_help.text="请准备好打印机!" filename="PRN" Case 2 filename="c:/simulate.txt" Case else Return End Choose fileno=FileOpen(filename,linemode!,write!,lockwrite!,replace!)

if fileno=-1

then

Messagebox("警告","设备文件<打印机>打开失败!",information!,ok!)

Return

endif

write_string="打印前的模拟显示!" FileWrite(fileno,write_string) FileClose(fileno)

 if Upper(filename)="PRN" then

sle_help.text="文件打印完毕!"

else

Run("Notepad.exec:/simulate.txt",maximized!) sle_help.text="模拟显示文件输出完毕!"

endif

这样,当需要模拟显示时,write_string被送往磁盘文件c:/simulate.txt,并调用Notepad将其显示出来;

当需要打印时,write_string被送往打印机。

 

一、实现运行窗口居中

  众所周知, PowerBuilder 7.0在以前的版本中提供了图形化的预览工具,可以很方便的在设计阶段实现窗口居中。但是,到了7.0似乎该项工具被取消了,也没有什么 属性来设置,至少我没有找到。每当我们需要将窗口居中的时候只有一点一点地调整它的X,Y值,这一点十分让人头痛。出于无奈,只好通过编程实现。我们只在每个需要居中的窗口的Open 事件中写上这些代码就会实现窗口的自动居中。
  要实现窗口的自动居中,我们可以用 函数Move()来实现。但是,由于我们开发出来的应用程序往往会在不同的设置环境中运行,虽然一般情况下是由我们为用户配置好的,但我们总不能保证用户显示器的设置模式不会改变。所幸的是,PB为我们提供了环境对象Environment,该对象其实是一个系统结构,其中保存了PB应用程序的运行平台信息,例如,代码集,CPU类型等。其中当然会有我们需要的屏幕宽和高。为了得到该结构我们可以用 函数GetEnvironment()。该 函数的作用是得到关于 操作系统,处理器和屏幕的系统信息。具体用法如下:

  Environment my_system
  Integer li_ScreenWidth, li_ScreenHeight
  GetEnvironment(my_system)
  // 得到屏幕的总宽度和长度
  li_ScreenWidth = PixelsToUnits(my_system.ScreenWidth, XPixelsToUnits!)
  li_ScreenHeight = PixelsToUnits(my_system.ScreenHeight, XPixelsToUnits!)
  // 函数PixelsToUnits()的作用是将屏幕的像数转换成 PowerBuilder使用的单位,因为像数通常不用于方形中,具体用法请参见 函数手册。
  然后就可以用Move()来实现窗口居中。

  This.Move((li_ScreenWidth - this.Width)/2, &
   (li_ScreenHeight - this.Height)/2 )
   二、数据窗口中实现字段的组合

  你有没有碰到这样的情况,我们输入了客户的详细地址资料,包括省份,城市,地址,邮编。当我们要给客户们邮寄信函时需要将他们的省份,城市,地址,邮编连成在一起打印出来。然而,这些信息是分开存储的,即存在不同的字段。这时我们就可以利用PowerBuiler提供的在数据窗口添加计算域(Compute Field)来实现字符字段的组合显示。
  现在假设客户的省份,城市,地址,邮编分别存放在不同的字段中,它们是Province,City,Address,PC。我们要得到“邮编+省份+城市+地址”的格式,如:“(214001)江苏省无锡市人民路1号。",具体实现如下:
  1、在需要显示的位置添加一个计算域(Compute Field)
  2、在它的表达式栏中写上“ '(' + PC + ' )' + Province + City + Address ”
  3、单击确定完成。
  很容易是不是。需要提醒大家的是,计算域只能用来显示,不能对它进行修改,因为它没有TAB 属性,不能得到焦点。
三、数据窗口中的条码显示

  在数据窗口中显示条码是不是很神秘?这对于没有接触过条码的人来说也许是这样。其实,它简单的不能再简单了。别忘了我们在写文稿时是如何使文稿看上去更有艺术化。没错我们会给它用上各种字体。问题不就解决了吗?条码也是种字体,只不过我们看不懂罢了。我们只要将需要用条码显示的字段的字体换上相应的条码字体就行了,比如,常用的3 of 9条码字库等。这些字体大多数是免费的,可以到网上去下载,然后安装就行了。

   四、数据窗口的自动刷新技术

  在我们编写诸如像库存,销售等应用系统时,总希望程序能动态的自动刷新库存量或销售量,比如说每隔1秒刷新一次。要实现这样的功能只要我们利用数据窗口的时间间隔 属性(Timer Interval),当该值为0时数据窗口不进行刷新,如果要使数据窗口以每一秒钟的频率刷新的话,只要将该值设为1000,即1000毫秒。
  我们还可以为应用程序添加闪烁报警的功能。就拿库存量来说吧,最常用的是当某货物的库存量达到一个最低库存量时程序应能自动判别,并用警告色显示,通常是红色。此时,我们只要在运用了上述的方法后再在需要闪烁的字段上,比如,库存量,在它的颜色 属性中写上相应的语句。下面这段代码实现“当某一物品的库存量小于20的时候,程序以红色闪烁显示警告”

  if ( Store_Num
  if mod( Second(Now()), 2)  0, & // 每秒一次,偶数显示红色,奇数显示白色,即底色
  RGB(255, 255, 255), RGB(255, 0, 0) )
   五、数据窗口中实现自动折行

  有时为了界面的考虑需要将一些长字段折行显示,但又不能确保在所有情况下都会出现长字段。比如,我们要求当客户地址超过50个字符时将它分两行显示,对于没满50个字符的客户仍然按照正常格式显示。
  具体如下:
  1、在数据窗口中选择要自动折行的列Address。
  2、选择位置 属性(Position)并选中Autosize Height选项
  3、 选择编辑 属性(Edit)并去掉Auto Horz Scroll选项
  4、选择数据窗口中细目带的 属性Autosize Height选项。注意,是当你拖动细目栏时出现的 属性。
  设置完毕后在左下角预览窗口中将显示图例的结果。
  在进行列自动折行显示的时候,还要注意一点,系统判断自动折行的断点是以空格或标点符号为依据的,如果我们输入一长串字符且中间没有任何标点符号或空隔的话,即使该字符串再长也无济于事。因此,我们一定要提醒用户在录入时适当的加入一些空格或回车。
   六、数据窗口中记录颜色的隔层显示

  相信你一定见过这样的显示,第一行记录的背景色是浅黄色,第二行记录的背景色是白色,第三行又是浅黄色,第四行又是白色……,依次类推。
  要想让显示的数据达到上述效果,我们首先要知道的是这些数据存放在数据窗口中的什么地方。很显然,数据窗口中的所有数据是在细目带中列出的,因此只要在细目带中设置一些数据 属性就会影响到这个细目带中的所有内容。具体实现如下:
  1、打开数据窗口,单击细目带(Detail),注意是点击有“Detail”字的一栏,不是它存放数据的地方。点击它鼠标会出现上下小箭头。
  2、选择常规 属性,单击颜色 属性(Color)旁边的一个带有红色图标的小按钮。
  3、在表达式一栏中输入
  if ( Mod( GetRow(), 2) = 0, RGB(255, 255, 200), RGB(255, 255, 255))
  // RGB(255, 255, 200),浅黄色
  OK,预栏一下,效果不错,大功告成。
七、实现数据窗口的动态排序

  在用到Windows的资源管理器的时候,当我们需要按照文件的类型或名称排序的时候只要用鼠标点击一下相应的标题栏,系统会按照升序或降序交替排序,这完全取决于用户鼠标点击的奇偶数。如此方便的操作何不用到我们的数据窗口中——实现按某列的动态排序。

  为了实现上述功能,程序首先需要用到一个全局变量(相对于PB来说可以用窗体变量),用它来控制连续两次点击之间的排序方向。因为字段的排序方向只有升序和降序两种,所以我们可以用一个Boolean型。本例中是Boolean ib_flag。接着在数据窗口控件的Clicked 事件中写上如下代码

  /*****************************程序代码******************************/
  String ls_column, ls_sort
  ls_column = this.GetObjectAtPointer() // 得到鼠标按下位置的对象名
  ls_column = left(ls_column, len(ls_column) - 2) // 得到列标题名字
  if not Lower(right(ls_column, 2)) = "_t" then return
  // 如果不是缺省的列标题,即以“_t”结尾或鼠标根本没有点在列标题上则退出
  ls_column = left(ls_column, len(ls_column) - 2) // 去掉列标题末尾的“_t”,得到有效的列标题名
  // 进行升序和降序的切换
  if ib_flag then
   ls_sort = ls_column + " A" // 升序
  else
   ls_sort = ls_column + " D" // 降序
  end if
  ib_flag = not ib_flag // 为下一次排序准备
  ls_sort = ls_sort
  this.setsort(ls_sort)
  this.sort()
  /*****************************结束******************************/
  注:这里所叙述的数据窗口中列的动态排序是以系统默认的列标题名为前提,即在标题栏中列标题名字是以“_t”结尾。
   八、设置系统时间

  在开发具有数据服务器的应用软件时,有时为了使所有计算机的时间都保持相同,或者接近相同,主要是为了避免数据录入时出现的时间差异,通常我们会在客户机每次使用应用软件前将本地时间设置成跟服务器相同。从而确保输入的数据在时间上具有较高的正确性。即,要实现系统时间的设置。下面就讲述一下实现的过程。
  本程序是通过调用外部 函数SetSystemTime()实现系统时间的设置,该 函数返回布尔型。查阅一下有关参考手册就会发现该 函数需要一个表示系统时间结构(Struct)的数据类型作为参数。此结构的定义如下:

  struct _SYSTEMTIME {
    INT iYear;
    INT iMonth;
    INT iDayOfWeek;
    INT iDay;
    INT iHour;
    INT iMinute;
    INT iSecond;
    INT iMilliseconds; // 毫秒
  } str_SystemTime;
  要在程序中使用外部 函数首先需要要进行外部 函数声明,可以根据 函数的使用范围放在全局外部 函数声明区或局部外部 函数区声明。具体声明如下:
  Function Boolean SetSystemTime(str_SystemTime str_MyTime) Library "Kernel32.dll"
  声明之后就可以使用了。下面代码实现将系统日期设置成“2000年8月10日”

  /*****************************程序代码******************************/
  str_SystemTime str_MyTime
  // 给结构赋值
  str_MyTime.iYear = 2000 // 2000年
  str_MyTime.imonth = 8 / /8月
  str_MyTime.iDay = 10 // 10日
  // 设置系统日期
  if SetSystemTime(ref str_MyTime)then
   Beep(1)
   MessageBox("提示","系统日期设置成功!")
  else
   Beep(1)
   MessageBox("提示","系统日期设置失败!")
  end if
  /*****************************结束******************************/
  注意,如果只有日期没有时间的话系统会将时间设置成默认的上午8点整。设置系统的时间方法和设置日期类似,只要将结构中的时、分、秒分别设置成想要的就行了。具体实现请读者自己完成。
  至此,本文所讲述的八个技巧到此结束。

六、结语写作本文不仅想与读者交流编程技巧,还希望向读者提供一些有用的思路。PowerBuilder不仅支持ODBC,而且支持MAPI,是企业级信息管理系统和Intranet应用开发的强大工具。笔者深信,PowerBuilder的应用范围将更加广泛,随着应用的深化,必定会涌现出更多优秀的PowerBuilder应用,并将在各行各业充分发挥作用。

 

你可能感兴趣的:(编程,数据库,timer,String,Sybase,PowerBuilder)