首页 > 代码库 > PB数据窗口其他知识

PB数据窗口其他知识

////////////////////////////////
标识当前行
〓标识当前行也就是给当前行加上醒目的标记,以便用户更清楚当前要对哪一行数据进行操作,尤其当用户的操作中间有停顿时,继续进行操作就特别需要知道哪个是当前行。这里的当前行不要狭隘地理解成仅仅是光标所在行。当翻页时如果光标所在行不在当前页中,这时应该将当前页中的第一行置为当前行,否则容易造成错觉;当删除数据时,不能自动改变当前行,此时应该使用脚本设置当前行。
标识当前行的函数和方法很多,比较常用的有SelectRow,SetRowFocusIndicaTor,数据窗口对象中的函数CurrentRow和GetRow等,还有很多可以在数据窗口对象中实现的方法。


〓使用SelectRow函数
这是最常用的一种方法。函数SelectRow的语法比较简单,重点要掌握需要在哪些事件中编写脚本。函数SelectRow的语法是:
dw_1.SelectRow(row,boolean)
其中,dw_1是数据窗口控件名称;row是要操作的行号,如果取值为0则表示对数据窗口dw_1中的所有数据进行操作;boolean表示是否选中数据,如果为True则表示选中row指定的行,如果为False则表示取消选中row指定的行。函数正确执行返回1,否则返回-1,如果有参数为NULL则返回NULL。
当选中数据窗口中的一行数据时,数据行以蓝底白字显示。当用户在数据窗口中使用上下光标键、或者Tab键、或者鼠标点击时,如果改变了光标所在行号则这些操作都会触发数据窗口控件的RowFocusChanged事件。所以,在数据窗口控件的RowFocusChanged事件中编写脚本来标识当前行,比仅在数据窗口的Clicked事件中编写脚本要全面得多。脚本如下:
if currentrow > 0 then
dw_1.selectrow(0,false)
dw_1.selectrow(currentrow,true)
end if
另外,由于滚动垂直滚动条操作不能触发RowFocusChanged事件,如果翻页后当前行不在当前页中,这样容易造成错觉,应该将当前页中的第一行设置为当前行。在数据窗口控件的ScrollVertical事件中编写如下脚本:
long ll_row
ll_row = long(describe("datawindow.firstrowonpage")) //当前页中第一行的行号
if ll_row < 1 then return
setrow(ll_row)
通过上面两个事件(RowFocusChanged,ScrollVertical)中编写的脚本,就可以较为全面地修改数据窗口控件的当前行标识。
*对“插入”按键编写脚本时,应该使用可以自动触发数据窗口RowFocusChanged事件的语句,例如使用dw_1.scrolltorow(dw_1.insertrow(0))语句可以自动修改当前行标识,而仅使用dw_1.insertrow(0)语句就不能自动修改行标识。
long ll_row
ll_row = dw_1.insertrow(0) //插入新行
dw_1.scrolltorow(ll_row) //滚动到新行
dw_1.setrow(ll_row) //设置新行为当前行
dw_1.setcolumn(2) //设置第2列为当前列
dw_1.setfocus( ) //dw_1获得焦点


〓使用函数SetRowFocusIndicaTor
函数SetRowFocusIndicaTor可以给当前行指定的位置上设置指定的标志,函数的语法格式如下:
dw_1.SetRowFocusIndicaTor(focusindiacaTor{,xlocation{,ylocation}})
其中,dw_1为数据窗口控件名称;focusindicaTor是枚举型或者特定的图片的名字,可以是以下取值:
Off!:取消行标识
FocusRect!:在当前行的周围放置一个虚线构成的矩形边框
Hand!:使用PB提供的手形指示器
图片的名字:使用图片的名字可以选择用户喜欢的行标识符号
该函数正确执行则返回1,否则返回-1,如果有参数为NULL则返回NULL。该函数正确执行后,当数据窗口控件中的RowFocusChanged事件触发时将自动给当前行设置行标。
使用该函数时,只需要在适当的时候个数据窗口控件设置行标识即可,一般可以在检索之后马上设置。例如,在窗口的open事件中可以如下编写脚本:
dw_1.settransobject(sqlca)
if dw_1.retrieve() > 0 then
dw_1.setrowfocusindicator(hand!)
dw_1.setrow(1)
end if
在ScrolVertical事件中编写的脚本和上面的“使用SelectRow函数”中介绍的完全相同。


〓使用CurrentRow和GetRow两个函数
在数据窗口对象中实现标识当前数据行的方法和在窗口中编程类似,也是通过数据窗口控件的RowfocusChanged事件来触发函数,只是触发的是数据窗口对象中的函数。所以,在数据窗口对象中放置函数后,还得在窗口的ScrollVertical事件中编写脚本,事件和上面介绍的完全相同。程序运行时的效果和在数据窗口对象中编程效果完全相同。
GetRow()函数返回的是表达式触发的行号,CurrentRow函数返回的是当前行的行号,他们都没有参数,返回值都是long类型的行号。使用这两个函数并配合其他的部件可以标识当前行。
通过修改当前行上text控件的可视性实现标识当前行,给用户的感觉好象当前行的边框发生了改变。首先,在Detail band中放置一个Text控件,删除该控件中的“text”,设置其外观为 3d Rasied,将其背景修改为和字段中的数据不同的颜色,将Text控件的sent to back选中,在该Text控件的属性视窗中为visible属性输入如下表达式:
if(currentrow() = getrow(),1,0)
该表达式的作用是在当前行获得焦点时显示Text控件,否则禁止显示Text控件,加宽该Text控件使其正好能够覆盖所有的字段,最好Text的边框能够显示出来,不被字段覆盖;设置所有字段的边框类型以增强显示效果,设置为无边框。注意,如果字段边框和Text边框设置不当,则显示效果不一定明显。
这种显示风格可以使用户很清楚当前要处理的是哪一行,尤其在处理工作中间有停顿的情况下特别有用。当然,要观看这种效果,数据窗口中至少应该有一个字段的Tab Order值不为0,否则不能改变当前行,也就无法获得运行效果了。
对上面的方法稍微变通一下,可以直接为所有要显示的字段的Border属性定义表达式。当前行获得焦点时所有字段是一种边框风格,当失去焦点时又是另外一种风格。这样也可以实现标识当前行,但是如果字段较多,设置起来就比较烦琐。比如,可以为所有字段定义如下表达式:
if(currentrow()=getrow(),1,0)
该表达式的含义是当前行字段的边框类型为1(即Shadowbox类型),非当前行则为0(即无边框)。


〓改变背景或者前景
和上面的方法类似,也是利用GetRow和CurrentRow两个函数在数据窗口对象中实现改变背景或前景颜色。需要为每个字段都规定前景和背景颜色,在每个字段的Background.color属性中都输入表达式:
if(getrow()=currentrow(),rgb(255,0,0),rgb(255,255,255))
然后,在数据窗口控件的ScrollVertical事件中编写和上述作用SelectRow函数中介绍的完全相同的代码即可。

////////////////////////////////
显示指定条件的数据
将符合特定条件的数据全部显示在数据窗口中,在大多数情况下是可以的,这不需要做什么工作。但是要同时将符合特定条件的和不符合这些条件的数据显示在数据窗口中,以便比较分析,这就需要一定的编程。关键是使用一定的方法来标识符合条件的数据。

※通过修改前景、背景颜色
要以特殊背景(或前景)颜色显示符合固定条件的数据行时,这种情况比较简单。可以在数据窗口创建时在字段的属性中把前景和背景颜色赋予固定的表达式。比如某数据窗口要醒目显示女职工信息,正常显示为黑色前景、灰色背景,醒目显示为红色前景、灰色背景,可输入如下表达式:
BackGroundColor:If(sex="女",RGB(255,0,0),RGB(0,255,0))
当需要特殊显示背景(或前景)颜色的数据行的条件是由用户动态指定时,就需要做一些编程工作。在用户改变查询条件的操作中,同时动态修改数据窗口的前景和背景表达式,这样就可以做到背景颜色和条件一起变化。例如,在窗口w_browse中有两个控件:下拉列表框ddlb_filter用来列出数据库中所有的职称;数据窗口dw_1显示相应的数据。当用户在下拉列表框ddlb_filter中选择不同的职称(在数据窗口中职称字段名称为zc)时,数据窗口dw_1中就用特殊背景颜色标识相应职称的职工数据,可在下拉列表框ddlb_filter的SelectionChanged事件中编写如下脚本来实现该功能:
Int li_count,li_index
String ls_ErrMsg
dw_1.SetRedraw(False)
//首先得到该数据窗口的列数
li_count = Integer(dw_1.Object.DataWindow.Column.Count)
//对每一列的前景、背景表达式进行修改
For li_index = 1 To li_count
ls_ErrMsg = dw_1.ModIfy("#" + String(li_index) +".Background.Mode = 0")
If ls_ErrMsg <> "" Then
MessageBox("Status", "Change To Background Mode Failed " +ls_ErrMsg)
Return
End If
ls_ErrMsg = dw_1.ModIfy("#" + String(li_index) + ".Background.Color= ~"16777215~tIf (zc = ‘" + This.Text +"‘,255,16777215)~"")
If ls_ErrMsg <> "" Then
MessageBox("提示","不能改变字段的背景颜色~r~n" + ls_ErrMsg)
End If
Next
dw_1.SetRedraw(True)

※通过修改设置位图
通过修改设置位图显示指定条件的数据时也有查询条件固定和不固定两种情况。对于固定条件的可以在数据窗口创建时输入位图的Visible表达式,对于条件不固定的可以在用户更换查询条件后使用函数ModIfy()动态地修改数据窗口中位图属性的表达式来实现。
在数据窗口的最左边放置一个位图部件,并修改其Visible属性为一个条件表达式即可。例如,在显示职工数据的数据窗口对象中放置picture控件后,需要在所有女职工数据行上显示该picture,可以在定义picture的Visible属性中输入如下表达式:If(sex=‘女‘,1,0)

※通过修改边框类型
当查询条件固定时,可以在数据窗口创建时输入边框(Border)表达式。比如,可以在字段的Border属性中输入如下表达式来标识女职工:If(sex=‘女‘,5,0)
当字段比较多时逐个设置所有的字段比较烦琐。可以像前面介绍的那样,通过放置一个可以覆盖所有字段的计算域、并为该计算域的Visible属性定义相关的表达式即可。注意,需要将该计算域的数据颜色和背景颜色设置为相同颜色,并且边框类型设置为和字段反差比较大的类型,比如,当字段都没有边框类型时将该计算域的边框设置为Rasied类型。这样也可以实现类似的效果。比如,要标识所有女职工,可以定义计算域的Visible属性为如下表达式:If(sex=‘女‘,1,0)
*这里只能放置计算域,或者放置数据表中其他不需要显示数据的字段,而不能放置其他控件。因为,需要为每一行创建一个对象,每一行相同的控件的属性是相同的,而不同行的字段或者计算域的属性可以不同。

※通过选中标记来标识符合条件的行
对所有符合特定条件的数据逐行选中,以选中标记来标识符合特定条件的数据。比如,可以为用户提供一个查询窗口,在该窗口中用户选择了不同的查询条件后,首先使其他所有行处于不选中状态,然后再根据用户条件选中所有符合条件的数据行。比如下拉列表框ddlb_filter中放的是数据库中所有职工的职称(zc),用户选择不同的职称可以在数据窗口dw_1中用选中标记来标识具有该职称的所有职工的数据。在下拉列表框的SelectChanged事件中编写如下脚本:
Int li_index
dw_1.SelectRow(0,False)
For li_index = 1 To dw_1.RowCount()
If dw_1.GetItemString(li_index,"name") = This.Text Then
dw_1.SelectRow(li_index,True)
End If
Next

////////////////////////////////
用回车键代替Tab键
创建一用户事件来响应数据窗口的pbm_dwnprocessenter事件,在该事件中加入下面程序:
Send(Handle(this),256,9,long(0,0)) //发送处理Tab键的信息
Return 1 //忽略Enter键的处理
例如,下面的脚本在相应数据窗口的pbm_dwnprocessenter用户事件中编写,用来检查数据窗口中录入数据的各种情况,并执行相应的程序。
(1)当返回负值时
肯定某列存在错误,这时既不处理Enter键也不处理Tab键。
(2)当返回非负值时
a)如果是最后的行列则应该在按回车键时插入一行,并使光标定位到新行的第一列上。
b)如果不是最后行列则应该发送Tab信息,屏蔽Enter键的处理。
if this.accepttext() < 0 then //如果不能正确接受用户的输入信息
return 1 //则不进行按键处理,直接返回
end if
if this.GetColomn() = long(dw_1.object.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)) //发送处理Tab键的消息
return 1 //忽略回车键的处理

////////////////////////////////
数据拷贝
〓使用GetItem()和SetItem()函数
在循环中将源数据窗口中的数据逐一读出,每读出一个字段就将其写入到目标数据窗口中的相应字段中,直到将源数据窗口中要拷贝的数据读完为止。这是经常使用的一种方法,虽然它的效率比较低,但控制比较灵活,允许有选择地拷贝某些字段,并在拷贝后可以进行一定的处理后再进行复制。这种方法在拷贝内容比较少时经常使用。

〓使用剪贴板
先使用函数SaveAs()将数据保存到剪贴扳上,再输入到另一个数据窗口中。这种方法适用于拷贝源数据窗口中的所有数据的情况,并且目标数据窗口兼容源数据窗口中对应字段的
数据类型。
dw_source.SaveAs("",Clipboard!,false) //将数据保存到剪贴板,--函数说明见帮助
dw_dest.ImportClipBoard() //将数据从剪贴板拷贝到目标数据窗口

〓使用结构类型的数组进行赋值
将数据从源数据窗口拷贝到结构类型的数组中,然后在从结构数组中将数据拷贝到目标致据窗口中。不采用下一小节所讲的直接赋值的方法,原因是有可能将提取出的数据有选择
地或者进行一番处理后再给目标数据窗口。
比如,下面定义一个变长的结构类型数组lstr_data,其结构和源数据窗口dw_source的字段构成相同,使用它进行数据拷贝。脚本如下:
lstr_data = http://www.mamicode.com/dw_source.objcet.data
dw_dest.object.data = http://www.mamicode.com/lstr_data

〓直接赋值
当两个数据窗口的对应字段类型相同或兼容时,可以将源数据窗口的数据直接赋给目标数据窗口。例如,将数据窗口dw_source中的数据全部拷贝到数据窗口dw_dest中,可以使用下面的脚本:
dw_dest.object.data = http://www.mamicode.com/dw_source.object.data
当需要拷贝数据窗口中的所有数据时,这种方法的执行效率最高。

〓使用行拷贝的方法
以行为单位,可以将数据拷贝到其他数据窗口中,也可以在一个数据窗口内部进行拷贝。使用函数RowsCopy可以实现数据行的拷贝。该函数的语法如下:
dw_1.RowsCopy(startrow,endrow,copybuffer,targetdw,beforerow,targetbuffer)
其中,dw_1为数据窗口控件名称;startrow,endrow和copybuffer参数都是对源数据窗口而言的,这三个参数指定源数据窗口copybuffer缓冲区中从startrow开始到endrow结束的数据要拷贝;参数targetdw,beforerow和targetbuffer用来限定要拷贝的数据放置到目标数据窗口的什么位置,即在目标数据窗口targetdw的targetbuffer缓冲区中从beforerow行开始放置拷贝过来的数据。函数正确执行返回1,否则返回-1,如果有参数为NULL则返回NULL。另外,缓冲区是一个枚举类型数据,应该取值为Primary!,Delete!或者Filter!。

〓其他
long dwcontrol.ImportString ( string string {, long startrow {, long endrow{,long startcolumn {, long endcolumn {, long dwstartcolumn } } } } } )
Description:Inserts data into a DataWindow control or DataStore fromtab-delimited data in a string.

long dwcontrol.ImportFile ( string filename {, long startrow {, longendrow {, long startcolumn {, long endcolumn {, long dwstartcolumn } } } } } )
Description:Inserts data into a DataWindow control or DataStore from a file.The data can be tab-delimited text or dBase format 2 or 3.

////////////////////////////////
运用External类型数据窗口
配置选项是指进行其他处理所需要的信息,当这些信息需要用户指定时,开发人员应该提供相应的界面,使用External类型数据窗口是一个很好的选择。虽然在窗口中放置控件也能实现,但是编程烦琐,要读取各控件,而且功能也不如External类型窗口强大。使用External类型的数据窗口还可以有效地保证界面的一致,需要同样功能的其他窗口只需简单地放置该数据窗口控件即可。比如,查询条件构造界面可以使用External类型数据窗口让用户来构造查询条件。
因为使用数据窗口来接受查询条件,只要使用Insert函数就可以增加查询条件,所以用户可以随意指定任意多个查询条件,而如果使用窗口中的下拉列表框或单行编辑器等控件就很难实现任意多个条件的组合。如果使用这些窗口控件,读取各个控件的值的方法也不统一,不同控件需要不同的读取方法,而使用External类型数据窗口只要用GetItemString一个函数就可以很方便地读出用户输入的条件。所以,灵活运用External类型数据窗口,不仅编程简洁,而且可以构造出功能强大的程序。
使用External类型的数据窗口取代窗口中的控件,关键在于灵活运用数据窗口对象中的各个控件和字段的编辑风格。

////////////////////////////////
数据保护
可以把数据的保护分为三种情况:
(1)某些字段不论什么条件下用户都不能修改,一般只是用来显示数据的
在什么条件下都不允许修改的字段一般可以通过程序设定,比如系统日期、依赖于其他字段的字段及按次序产生的序号等,这样的字段让用户修改就不容易保证数据的一致性和正确性,并且用户也没有必要修改,可以用以下三种方法将这样的字段设置为用户不可修改的字段:
a.在数据窗口设计时将这些字段的TabOrder值置为0,以使该字段不能获得焦点,用户无法选中和编辑该字段。
b.将字段的DisplayOnly属性设置为True。该字段可以获得焦点,可以选中该字段,还可以拷贝复制该字段中的内容,但不能编辑该字段中的内容。
c.设置字段的Protect属性。字段的Protect属性如果设置为“1”,则该字段的TabOrder值即使不是0也不会得到焦点。使用该属性可以保护一些重要的数据。
多用户下的、值按次序产生的关键字段可设置为不可修改。因为是关键字段,必须保证其惟一性。多个用户可能在同时修改该字段,靠前台就很难保证惟一性,只能通过后台修改,所以这样的字段需要设置为不可修改。在数据窗口画板中选择窗口菜单Rows中的Update Properties命令,把需要后台产生的字段在左下角的Updateable Columns中去掉,并在后台将该字段设置为序列字段即可。
(2)已有数据不让修改,新数据中的部分或全部允许修改
这种情况可通过编程实现。编程思想是:判断当前行的状态,如果是新增加的数据行则允许修改,否则不允许修改。是否可以修改可以通过设置字段的Protect属性来实现,当然还可以有更多的方法。是否是新增加的数据可以通过使用函数GetItemStatus判断行的状态来得知。具体编程如下:
a.为了恢复字段的Protect属性,定义一个实例变量,类型为整型变长数组:
Int ii_protect[] //保存字段的Protect属性
b.在窗口的Open事件中保存数据窗口所有字段的Protect属性:
Int li_ColumnCount
Long ll_index
li_ColumnCount = dw_1.Object.DataWindow.Column.Count
For ll_index = 1 To li_ColumnCount
ii_protect[ll_index] = dw_1.Describe("#" + String(ll_index) +".Protect")
Next
c.在数据窗口的RowFocusChanged事件中编程如下:
long li_ColumnCount
Int li_index
If CurrentRow <= 0 Then Return
dw_1.SetRedraw(False)
li_ColumnCount = Integer(dw_1.Object.DataWindow.Column.Count)
If dw_1.GetItemStatus(CurrentRow,0,Primary!) = New! Or &
dw_1.GetItemStatus(CurrentRow,0,Primary!) = NewModIfied! Then //新数据
For li_index = 1 To li_ColumnCount
dw_1.ModIfy("#" + String(li_index) + ".Protect = " + String(ii_protect[ll_index]))
Next
Else //旧数据,不允许修改
For li_index = 1 To li_columnCount
dw_1.ModIfy("#" + String(li_index) + ".Protect = 1")
Next
End If
dw_1.SetRedraw(True)
d.在Clicked事件中编程如下:
This.TriggerEvent(RowFocusChanged!)
(3)符合某种条件的数据允许修改,不符合该条件的数据不允许修改
符合某些条件的行才允许修改,可以在数据窗口设计中实现。在数据窗口设计时修改字段的Protect属性为条件判断表达式。比如,当岗位工资大于180时允许修改,可使用如下表达式:
If (gwgz > 180,0,1)
但是用户如何知道哪行可修改哪行不可以呢?可以使用前面介绍的标识特定条件的数据技术来标识这些不能修改的数据。例如,把可修改行的背景颜色改为红色,不可修改的背景改为灰色,可以在字段的BackgroudColor属性中输入表达式:
If(gwgz <= 180,RGB(192,192,192),RGB(255,0,0))

////////////////////////////////
数据操作的安全性
1.通过弹出窗口让用户确认
在数据删除之前,显示提示窗口让用户进一步确认是否真要删除数据。
2.设立删除数据恢复功能
当数据删除并且还没有提交到数据库之前,删除的数据保存在数据窗口的Deleted缓冲区中,可以使用函数从该缓冲区移动到Primary缓冲区中,以实现删除数据的恢复功能。函数RowsMove可以实现数据以行为单位的移动,其语法如下:
dwcontrol.RowsMove(startrow,endrow,movebuffer,targetdw,beForerow,targetbuffer)
其中,dwcontrol是源数据窗口控件名称;参数startrow,endrow和movebuffer都是对源数据窗口而言的,用来指定要移动的数据是movebuffer缓冲区中的从startrow开始到endrow结束的数据;targetdw是目标数据窗口控件名称,可以和源数据窗口控件相同;移动的数据放置在targetdw窗口targetbuffer缓冲区从beForerow开始的行中。该函数正确执行返回1,否则返回-1,如果有参数为NULL则返回NULL。
使用上面的函数可以实现删除数据的恢复功能。
*可以全部恢复,也可以一条一条的恢复
3.恢复到打开窗口时的最初状态
重新使用函数Retrieve()执行检索即可恢复到打开窗口时的最初状态。因为这是不可恢复性操作,检索将放弃对数据执行的所有操作,所以在重新执行检索之前一定要显示提示信息让用户确认。
4.窗口关闭时让用户确认对没有保存的数据如何处理
在窗口关闭时,如果数据窗口中的数据没有保存,一定要让用户确认如何处理,而不是直接关闭窗口。可以在窗口的CloseQuery事件中编写脚本,该事件是窗口关闭之前的最后一个事件。程序的设计思路是:
a.判断数据是否尚未保存:
如果已经保存,则允许关闭窗口,程序结束;
如果尚未保存,程序继续。
b.询问用户是否保存数据:
如果用户不保存,则允许关闭窗口,程序结束;
如果保存数据则调用函数Update提交数据,程序继续。
c.判断数据是否保存成功:
如果保存成功则关闭窗口,程序结束;
如果保存不成功,程序继续。
d.询问用户是否继续关闭窗口,根据用户的回答决定窗口是否关闭。
根据以上描述,程序脚本如下:
Int li_flag
If dw_1.ModIfiedCount() = 0 And dw_1.DeletedCount() = 0 Then Return 0
Beep(2)
li_flag = MessageBox("确认","数据已经修改,是否保存",Question!,YesNoCancel!,1)
If li_flag = 2 Then //用户选择了‘No‘
Rollback; //回退事务
Return 0 //关闭窗口
Elseif li_flag = 3 Then //用户选择了‘Cancel‘
Return 1 //不允许关闭窗口
End If
If dw_1.Update()=1 Then //如果保存成功
Commit; //提交数据
Return 0 //关闭窗口
Else //保存不成功
Rrollback; //回退事务
Beep(2) //响铃两声
//让用户确认是否关闭窗口
Li_flag = MessageBox("错误","数据保存不成功,是否继续?",Question!,YesNo!,2)
If li_flag = 1 Then Return 0 //关闭窗口
Return 1 //不关闭窗口
End If
上面的脚本也可以简化成以下语句:
If dw_1.ModIfiedCount() <> 0 Or dw_1.DeletedCount() <> 0 Then
Beep(2)
MessageBox("提示", "数据已经修改,请先保存数据再退出! ")
Else
Close(Parent)
End If

////////////////////////////////
数据窗口用做下拉列表框
把数据窗口当做下拉列表框来使用会收到意想不到的效果。使用下拉列表框具有如下缺点:
a.只能显示一行数据,提供的信息有限;
b.如果要显示的内容来自数据库,数据提取编程较麻烦,需要定义游标,提取数据速度较慢;
c.数据的动态更新不方便,动态提取源数据中符合某些条件的数据不方便。
使用数据窗口做下拉列表框可以弥补以上不足。在数据窗口上要做如下编程才能实现下拉列表框的效果:
a.如何选中和取消一行;
b.点按鼠标后如何使数据窗口有下拉效果,失去焦点后如何使数据窗口收起;
c.如何处理选中的数据。
现在分别解决这几个问题:
(1)编程思想是:判断鼠标点按的行是否已选中,如果已选中则取消该行,否则选中该行。
在数据窗口的Clicked事件中如下编程:
long ll_RowNo
ll_RowNo = dw_1.GetClickedRow()
If dw_1.IsSelected(ll_RowNo) Then
dw_1.SelectRow(ll_RowNo,False)
Else
dw_1.SelectRow(0,False)
dw_1.SelectRow(ll_RowNo,True)
End If
(2)为使数据窗口有下拉列表框的效果,在数据窗口的右面放置一箭头图标,编程思想是:用鼠标点按箭头图标时将数据窗口变长,焦点离开数据窗口时数据窗口变短。
在箭头图标的Clicked事件中如下编程:
If dw_1.height < 900 Then //判断数据窗口是否下拉
dw_1.Resize(846,900) //下拉数据窗口,长度变长
dw_1.VScrollBar = True //显示滚动条
dw_1.BringToTop = True
p_dwnarrow.Visible = False //隐含箭头图标
dw_1.SetFocus()
End If
在数据窗口的LostFocus事件中如下编程:
// 关闭下拉效果
This.VScrollBar = False
This.resize(846,89) //数据窗口恢复原来大小
p_dwnarrow.Visible = True //显示箭头图标
定义两个用户事件,分别用来处理用户的按钮操作:
e_process_enter_key:PBm_dwnprocessenter
keyup:pbm_keyup
在事件e_process_enter_key中如下编程:
This.TriggerEvent (Clicked!)
This.SetActionCode ( 1)
在事件keyup事件中如下编程:
This.PostEvent(LoseFocus!)
经过以上编程处理就可以得到有下拉列表框效果的数据窗口。
(3)最后处理选中的数据。这部分的编程比较自由,只要从头到尾扫描数据窗口,选出用户选中的数据,接下来的处理就得根据不同应用需要来决定。

////////////////////////////////
数据窗口用做列表框(包括多行选择)
数据窗口经常用来提取数据供用户查看或者让用户修改。但是如果把数据窗口当做列表框来使用会收到意想不到的效果。使用列表框具有如下缺点:
a.只能显示一行数据;
b.数据提取编程较麻烦,需要定义光标,提取数据速度较慢;
c.数据的动态更新不方便,动态提取源数据中符合某些条件的数据不方便。
使用数据窗口作为列表框可以弥补以上不足。但这并不是简单的代替,还得编程解决如下问题:
a.如何选中和取消一行;
b.如何选中多行;
c.如何处理选中的数据。
现在分别解决这几个问题。
#如何选中和取消一行
编程思想是:判断用户当前所点击的行是否已经选中,如果已选中则取消,否则选中该行。在数据窗口控件dw_1的Clicked事件中编程如下:
Long ll_RowNo //保存当前所击行
ll_RowNo = dw_1.GetClickedRow()
If ll_RowNo <= 0 Then Return
If dw_1.IsSelected(ll_RowNo) Then
dw_1.SelectRow(ll_RowNo,False)
Else
dw_1.SelectRow(0,False) //清除其他选中行
dw_1.SelectRow(ll_RowNo,True) //选中当前行
End If
#如何选中多行
多行选择时先做这样的假设:按下ShIft键后再单击某行时,当前行和上一次所选行之间的都选中;按下Ctrl键后再单击某行时,当前行选中,原来选中的行不清除;只用鼠标单击某行时,原来行清除后选中当前行。编程思想是:首先选中当前行,再判断哪个键按下,如果ShIft键按下则将上次所选行和当前行之间的都选中,如果Ctrl键按下则不做任何处理,如果没有键按下则清除原来所选行。
首先定义一个Instance变量:
Long il_LastRow = 0 //保存上一次所击行
在数据窗口的Clicked事件中编程如下:
Long Ll_CurRow,ll_index //Ll_CurRow保存当前行,i用作循环变量
Ll_CurRow = GetClickedRow()
If Ll_CurRow <= 0 Then Return //所击为非数据区,则直接返回
If KeyDown(KeyShIft!) Then //按下了ShIft键,进行多重选择
This.SelectRow(0,False) //清除原来所选数据
If il_LastRow <> 0 Then //已选中过数据
If Ll_CurRow > il_LastRow Then
For ll_index = il_LastRow To Ll_CurRow //逐行选中
This.SelectRow(ll_index,True)
Next
Else
For ll_index = Ll_CurRow To il_LastRow
This.SelectRow(ll_index,True)
Next
End If
Else //原来没有选中过数据
This.SelectRow(Ll_CurRow,True) //只选中当前行
End If
il_LastRow = Ll_CurRow //保存当前行
Elseif KeyDown(KeyControl!) Then //如果按下了Ctrl 键
If This.IsSelected(Ll_CurRow) Then //如果是已选中行
SelectRow(Ll_CurRow,False) //取消该行
Else //如果不是选中行
SelectRow(Ll_CurRow,True) //选中该行
End If
il_LastRow = Ll_CurRow //保存当前行
Else //什么键也没有按下
This.SelectRow(0,False) //取消其他所有行
This.SelectRow(Ll_CurRow,True) //选中当前行
il_LastRow = Ll_CurRow //保存当前行
End If
#如何处理选中的数据
允许多行选择时,从数据窗口的第一行扫描到末行,判断每行是否选中,如果选中则提取相应字段;只允许选择一行时只要取当前行中的相应字段即可。比如,下面的脚本读取数据窗口dw_1中所有选中行的name字段的取值:
Long ll_CurRow
String ls_name
For ll_CurRow = 1 To dw_1.RowCount
If dw_1.IsSelected(ll_CurRow) Then
ls_temp = dw_1.GetItemString(ll_CurRow, "name")
//再进行其他相关的处理
End If
Next
#还有一种提取所有选中数据的方法是使用数据窗口对象属性来获得。例如,要读取所有选中行的salary字段的值:
Long ld_salary[]
ld_salary = dw_1.Object.salary.Selected
//再进行其他相关的处理
经过以上选择处理,数据窗口已经完全具备了列表框的功能了,只需在窗口的Open事件中给数据窗口设置事务对象然后检索就可以提取数据了,当要动态改变其中的数据时,只要设置过滤规则进行过滤即可。因为可以在数据窗口控件中提供多行数据,其中一行作为对应列表框中的数据,其他作为解释或者提示信息即可,这样才能更大地发挥数据窗口作为列表框的优势。

////////////////////////////////
当数据窗口中的数据来自不同的数据表时,一般只让用户查看数据,或者只让用户修改其中一个数据表中的数据,这样可以不用编写任何脚本就能实现。但是,特殊情况下需要修
改多个数据表中的数据,因为数据窗口对象不能同时修改两个数据表,只能编写脚本来实现该功能。
用脚本实现一个数据窗口对象修改多个数据表的数据,编程思想是动态修改数据窗口的
Update相关属性。实际上,数据窗口修改数据表是根据在DataWindow画板中指定的属性进
行修改的,这些属性在运行时可以由脚本修改,从而让数据窗口修改完一个数据表后再修改另一个数据表,直到修改完所有需要修改的数据表。需要注意的一点是,数据窗口中每一行都有一个修改标志(可以使用函数GetItemStatus来读取该标志),当数据提交后数据窗口自动清除该标志,所以当需要修改多个数据表时,在没有修改完所有的数据表之前不能清除每一行的标志。函数dwcontrol.Update({accept{,resetflag}})中的参数resetflaf为boolean类型,取值为False表示不修改数据行的行标志。下面的脚本示例了在一个DataWindow中修改它所对应的多个表,该数据窗口的字段来自两个表,语句如下:
select department.dept_id,department.dept_name,employee.emp_id,employee.emp_fname,employee.emp_lnamefrom department,employee
在创建数据窗口时设置为Department表可修改。所以,程序运行时可以首先修改Department表,如果修改数据表成功,则进行第二个数据表的修改。首先设置要修改第二个数据表的哪些字段、哪些字段作为主键、要修改的数据表表名,然后使用update语句开始修改数据表。如果第二个数据表修改成功,则再修改第三个数据表,直到修改完所有的数据表。在本例中只有两个数据表要修改,脚本如下:
integer li_resultupdate
li_resultupdate = dw_1.update(true,false) //接受最后一个字段内容,并不清 //除行修改标志,如果对department表的修改成功,下一步就要修改另一个表employee
if li_resultupdate = 1 then
//首先,关掉对department表的修改
dw_1.modify(‘department_dept_name.update = "no"‘)
dw_1.modify(‘department_dept_id.update = "no"‘)
dw_1.modify(‘department_dept_id.key = "no"‘)
//使employee表成为新的可修改表
dw_1.modify(‘datawindow.table.updatetable = "employee"‘)
dw_1.modify(‘employee_emp_id.update = "yes"‘)
dw_1.modify(‘employee_emp_fname.update = "yes"‘)
dw_1.modify(‘employee_emp_lname.update = "yes"‘)
dw_1.modify(‘employee_emp_id.key = "yes"‘)
//然后保存employee表
li_resultupdate = dw_1.update()
if li_resultupdate = 1 then
commit;
else
messagebox("","数据库保存出错")
rollback;
end if
//恢复数据窗口开始时的属性,以便下一次用户点击“保存”时,程序能正确运行
dw_1.modify(‘department_dept_name.update = "yes"‘)
dw_1.modify(‘department_dept_id.update = "yes"‘)
dw_1.modify(‘department_dept_id.key = "yes"‘)
dw_1.modify(‘datawindow.table.updatetable = "department"‘)
dw_1.modify(‘employee_emp_id.update = "no"‘)
dw_1.modify(‘employee_emp_fname.update = "no"‘)
dw_1.modify(‘employee_emp_lname.update = "no"‘)
dw_1.modify(‘employee_emp_id.key = "no"‘)
else
messagebox("","数据保存错误")
rollback;
end if

////////////////////////////////