[返回]
中国计算机报2000年第92期

实现不同窗口中数据控件共享数据

秦天保

  PowerBuild PFC 类库提供的链接服务,以及保存更新的pfc_save( )事件功能强大。链接服务保证数据窗口控件间记录的协调滚动,而pfc_save( ) 事件内置了更新数据时的校验和出错处理,将程序员从烦琐的校验和出错处理编码中解放出来。但是,有些程序员发现若链接发生在不同窗口中的数据窗口控件,则pfc_save( )事件过程(closequery事件过程也存在类似问题 )不能正确地更新数据窗口控件。本文将以一个实例讨论如何解决这种问题。


  ■实例描述


  实际系统开发时常遇到一种“浏览-编辑”设计模式。如图1是产品浏览窗口(Main类型),当用户双击某行时,弹出图2所示的产品编辑窗口(Popup类型),在该窗口显示细节信息并可编辑。当在浏览窗口移动记录时,浮在上面的编辑窗口中的记录也跟着移动。在这里,编辑窗口中的数据窗口控件都链接到浏览窗口的数据窗口控件,并且与之共享缓冲区。

  图1

  本例中数据库是PowerBuild自带的easdemodb,各窗口构成如下:

  1.产品浏览窗口(Main类型)名称:w_product_list,含数据窗口控件dw_1,所链接的数据窗口为d_product_list(Grid类型),数据源为

  SELECT "product"."id","product"."name","product"."description","product"."prod_size",
"product"."color","product"."quantity", "product"."unit_price", product"."picture_name"

   FROM "product"

  2.产品编辑窗口(Popup类型)名称:w_product_edit,含一个Tab控件:tab_1,两个Tab页:tabpage_1、tabpage_2。tabpage_1上含数据窗口控件dw_1,链接到数据窗口d_product_edit1(Freedom类型),数据源同上;tabpage_2上含数据窗口控件dw_2,链接到数据窗口d_product_edit2(Freedom类型),数据源也同上。另外还有两个命令按钮cb_save、cb_close。

  注:以上所有窗口都继承自w_master,所有数据窗口控件继承自u_dw。而tab、tab页不必继承自PFC用户对象,只需用PowerBuild内置的即可。


  ■具体代码


  下面给出事件代码:

  w_product_list的open 事件:

  //设置数据窗口的更新属性,见第1点说明

  dw_1.of_setupdateable( false)

  //设置dw_1为只读,因为它是浏览窗口

  dw_1.object.datawindow.readonly = 'yes'

  dw_1.setrowfocusindicator( hand!)

  dw_1.of_settransobject (sqlca)

  dw_1.of_retrieve ()

  

  w_product_list的close 事件:

  //关闭编辑窗口

  if isvalid (w_product_edit) then

  close (w_product_edit)

  end if

  

  w_product_edit的pfc_endtran 事件:

  //本事件由pfc_save过程调用,用于提交或回滚更新

  if ai_update_results = 1 then

  li_return = sqlca.of_commit()

  else

  li_return = sqlca.of_rollback()

  end if

  if li_return = 0 then

  return 1

  else

  return -1

  end if

  

  w_product_list.dw_1的doubleclicked事件:

  //打开w_product_edit窗口,并同步三个数据窗口控件

  if not isvalid (w_product_edit) then

   open (w_product_edit )

   this.scrolltorow ( row )

   w_product_edit.tab_1.tabpage_1.dw_1.scrolltorow ( row )

   w_product_edit.tab_1.tabpage_2.dw_2.scrolltorow ( row )

  end if

  

  w_product_list.dw_1的pfc_retrieve事件:

  return this.retrieve ( )

  

  w_product_edit.cb_close的clicked 事件:

  close ( parent )

  

  w_product_edit.cb_save的clicked 事件:

  //更新数据,pfc_save ()是pfc事件,已内置了所需的更新处理代码。理解其代码是理解pfc的关键,值得好好研究

  parent.event pfc_save ()

  

  w_product_edit的open 事件:

  //设置数据窗口控件的更新属性,见下面第1点说明

   tab_1.tabpage_1.dw_1.of_setupdateable(true)

   tab_1.tabpage_1.dw_1.of_setupdateable(false)

  //启用链接服务

  w_product_list.dw_1.of_setlinkage( true)

  tab_1.tabpage_1.dw_1.of_setlinkage( true)

  tab_1.tabpage_1.dw_1.inv_linkage.of_setmaster( z_w_product_list2.dw_1)

  tab_1.tabpage_1.dw_1.inv_linkage.of_register( 'id', 'id')

  tab_1.tabpage_1.dw_1.inv_linkage.of_setstyle(tab_1.tabpage_1.dw_1.inv_linkage.scroll)

  tab_1.tabpage_2.dw_2.of_setlinkage( true)

  tab_1.tabpage_2.dw_2.inv_linkage.of_setmaster( z_w_product_list2.dw_1)

  tab_1.tabpage_2.dw_2.inv_linkage.of_register( 'id', 'id')

  tab_1.tabpage_2.dw_2.inv_linkage.of_setstyle( tab_1.tabpage_2.dw_2.inv_linkage.scroll)

  //共享

  w_product_list.dw_1.sharedata (tab_1.tabpage_1.dw_1)

  w_product_list.dw_1.sharedata (tab_1.tabpage_2.dw_2)

  //设置更新对象,见下面第2点说明

  u_dw ldw [ ]

  ldw [1 ] = w_product_list.dw_1

  ldw [2 ] = tab_1.tabpage_1.dw_1

  ldw [3 ] = tab_1.tabpage_2.dw_2

  of_setupdateobjects (ldw)

  //注:由于设置了共享,所以此处不必调用w_product_list.dw_1.inv_linkage.of_settransobject ( sqlca )为子数据窗口控件设置事务对象,也不必在两个数据窗口控件的pfc_retrieve事件中写return this.retrieve()代码。


  ■程序说明


  以上代码中关键之处说明如下:

  1.首先,要将三个共享的数据窗口控件中的两个通过of_setupdateable(false )函数设置为不可更新,只允许一个被更新。如w_product_list的open 事件中的dw_1.of_setupdateable( false),w_product_edit的open 事件中的tab_1.tabpage_1.dw_1. of_setupdateable( true ),tab_1.tabpage_1.dw_1. of_setupdateable( false )。若不如此设置,当pfc试图依次更新这三个共享的数据窗口控件时,第二个数据窗口控件对数据库的更新会覆盖第一个数据窗口控件对数据库的更新,此时,系统会产生运行错误,以防止第一个数据窗口控件的更新丢失(类似于多个用户同时更新数据库时的情况)。通过如上设置,pfc将只更新一个数据窗口控件,而由于三个数据窗口控件是共享的,所以这等于同时更新了三个数据窗口控件。

  图2

  2.其次,在w_product_edit的open 事件中要调用of_setupdateobjects( )函数设置更新对象(参见w_product_edit的open 事件代码)。实际上,该函数的名称有些迷惑人。用它设置的数据窗口控件并非一定会被更新,但这些数据窗口控件一定会被执行accepttext( )函数,并会被校验。这样设置的目的就是要三个数据窗口控件都接受校验(因为用户可能在三个数据窗口控件中都修改了数据,所以应该都执行校验)。读者可能会奇怪,就是产品浏览窗口的数据窗口控件已设置为只读,用户不可能编辑它,所以没必要校验,为什么还要将它包括在更新对象中呢?这是由pfc_save事件的处理特点决定的。首先,链接的数据窗口控件构成一个链接链,链接链的更新要由顶层数据窗口控件控制。而如果不将浏览窗口中的数据窗口控件(它是顶层数据窗口控件)包含进of_setupdateobjects ( powerobject apo_objects[])的数组参数中,pfc_save事件过程将无法将链接链的更新控制权交给它,从而也就无法更新链接链中的任何数据窗口控件。所以必须将顶层数据窗口控件包含进更新数组中。

  以上这两个步骤的意义在于既让pfc校验所有共享的数据窗口控件,又让它最终只更新一个数据窗口控件(这等于也将其它共享的数据窗口控件也更新了)。


  ■结论


  最后,做个总结:在pfc应用中,如果有多个数据窗口控件共享数据,不管是在同一窗口中还是在不同窗口中,也不管是否存在链接关系(尽管本例用了链接服务,但这不是必需的),通常应先利用编辑窗口(不是数据窗口控件)的of_setupdateobjects(powerobject apo_objects[])函数设置更新对象,将所有可能被用户编辑的共享的数据窗口控件传递到该函数的数组参数中(如果使用了链接服务,则不管顶层数据窗口控件是否被编辑,都应该将其包括进更新对象数组参数,如本例中那样)。然后对每个参与共享的数据窗口控件,调用其of_setupdateable( boolean ab_isupdateable)函数,注意仅将其中一个数据窗口控件设置为of_setupdateable(true),其它的数据窗口控件全设置为of_setupdateable(false)。这样,就可保证既校验所有可能被编辑的数据窗口控件,又兴地出现更新错误。