PB数据窗体其它知识

////////////////////////////////
标识当前行
〓标识当前行也就是给当前行加上醒目的标记。以便用户更清楚当前要对哪一行数据进行操作,尤其当用户的操作中间有停顿时,继续进行操作就特别须要知道哪个是当前行。这里的当前行不要狭隘地理解成仅仅是光标所在行。当翻页时假设光标所在行不在当前页中。这时应该将当前页中的第一行置为当前行,否则easy造成错觉;当删除数据时。不能自己主动改变当前行,此时应该使用脚本设置当前行。
标识当前行的函数和方法非常多,比較经常使用的有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事件。假设翻页后当前行不在当前页中,这样easy造成错觉,应该将当前页中的第一行设置为当前行。在数据窗体控件的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 = dw_source.objcet.data
dw_dest.object.data = lstr_data

〓直接赋值
当两个数据窗体的对应字段类型同样或兼容时。能够将源数据窗体的数据直接赋给目标数据窗体。比如。将数据窗体dw_source中的数据全部复制到数据窗体dw_dest中。能够使用以下的脚本:
dw_dest.object.data = 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)某些字段不论什么条件下用户都不能改动。一般仅仅是用来显示数据的
在什么条件下都不同意改动的字段一般能够通过程序设定,比方系统日期、依赖于其它字段的字段及按次序产生的序号等,这样的字段让用户改动就不easy保证数据的一致性和正确性,而且用户也没有必要改动,能够用以下三种方法将这样的字段设置为用户不可改动的字段:
a.在数据窗体设计时将这些字段的TabOrder值置为0,以使该字段不能获得焦点。用户无法选中和编辑该字段。
b.将字段的DisplayOnly属性设置为True。该字段能够获得焦点,能够选中该字段,还能够拷贝复制该字段中的内容,但不能编辑该字段中的内容。
c.设置字段的Protect属性。字段的Protect属性假设设置为“1”。则该字段的TabOrder值即使不是0也不会得到焦点。

使用该属性能够保护一些重要的数据。


多用户下的、值按次序产生的keyword段可设置为不可改动。

由于是keyword段。必须保证其惟一性。多个用户可能在同一时候改动该字段,靠前台就非常难保证惟一性,仅仅能通过后台改动,所以这样的字段须要设置为不可改动。

在数据窗体画板中选择窗体菜单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 //显示箭头图标
定义两个用户事件。分别用来处理用户的button操作:
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

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

 

posted on 2019-05-12 08:05  xfgnongmin  阅读(755)  评论(0编辑  收藏  举报

导航