PowerBuilder – Next Key Service
As an extension of the next key stored procedure article, here is a pretty easy to implement a next surrogate key service to use with the PFC. If you don’t use the PFC it is still fairly easy to use with some modification. You can use this service anytime you want to get a sequential number, such as a document number or a sequence number. These services use a DataStore to call the spcmn_getNextKeyResultSet stored procedure, which returns the next key for the specified table.
Next Key Class (n_cst_nextKey)
This is the base class that adds “Next Key” functionality to a PFC based PowerBuilder application. This class can be called directly by the developer but is mainly used by the “Next Key” Application, DataWindow, and DataStore services.
Example usage
// reference to the next key object n_cst_nextKey lnv_nextKey // create the next key object lnv_nextKey = create n_cst_nextKey // set the next key's trans object; note: this step is not required as SQLCA is the default lnv_nextKey.of_setTransObject (SQLCA) // example: getting next key for "Table" long ll_key ll_key = lnv_nextKey.of_getNextKey ("Table") // example: getting next 10 keys for "Table" long ll_key ll_key = lnv_nextKey.of_getNextKey ("Table", 10) // destroy the object destroy (lnv_nextKey)
Next Key Application Service (n_cst_nextKey instance variable added to n_cst_appmanager)
This service adds the Next Key class to the PFC application manager class, n_cst_appmanager. When activated, this service adds next key functionality across an entire application. One benefit is that it is instantiated only once and persists the life of the application; therefore, there is very little overhead involved with using this service.
Example usage
In the constructor event of the application manager (typically a descendant of n_cst_appmanager).
// start the next key service of_setNextKey (true)
After a connection to the database is established, call of_setTransObject( ) to set the trans object. This method need only be called for trans objects other than SQLCA since SQLCA is the default.
// set the next key's trans object; note: this step is not required as SQLCA is the default gnv_app.inv_nextKey.of_setTransObject (ltr_object)
From here on out, the next key service can be called from anywhere in the application with a single line.
// example: getting next key for "Table" long ll_nextKey ll_nextKey = gnv_app.inv_nextKey.of_getNextKey ("Table") // example: getting next 10 keys for "Table" long ll_nextKey ll_nextKey = gnv_app.inv_nextKey.of_getNextKey ("Table", 10)
Next Key DataWindow Service (n_cst_dwsrv_nextKey added to u_dw)
This service adds the Next Key class to the PFC DataWindow control class, u_dw. When activated, this service adds next key functionality for a specific DataWindow control. The main benefit with this service is that key columns in a dataobject can be registered to a table in the constructor event of a DataWindow control, saving the developer from writing the same code for each DataWindow in the application.
There are two ways to register columns with tables: automatic and manual.
Automatic registering allows the writing of generic objects. Registering is done through an assignment in the tag value of a column (e.g., “nextkey=Table”). With automatic registering, of_register( ) is called with no arguments.
Manual registering requires the calling of of_register( ) with explicit table names and column names. There is less overhead with this method because the Next Key class does not have to hunt through tag values columns and tables to register.
Example usage
In the constructor event of the DataWindow control.
// start the next key service of_setNextKey(true) // set the next key's trans object; note: this step is not required as SQLCA is the default inv_nextKey.of_setTransObject (ltr_object)
Manual registering
// manually register columns to tables inv_nextKey.of_register ("Table", "Column") inv_nextKey.of_register ("Table2", "Column2")
Automatic registering
// automatically register columns to tables based on dataobject tag values // "Column" has tag = 'nextkey=Table' // "Column2" has tag = 'nextkey=Table2' inv_nextKey.of_register( )
Some advanced examples of getting keys:
// get next key and apply to all rows in a DataWindow dw_test.inv_nextKey.of_getNextKey("Table", "Column") // get next key and apply to selected rows in a DataWindow based on an expression dw_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)") // get next key and apply to all rows in a DataWindow, without remembering the row status dw_test.inv_nextKey.of_getNextKey("Table", "Column", false) // get next key and apply to selected rows in a DataWindow based on an expression, without remembering the row status dw_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)", false)
Next Key DataStore Service (n_cst_dssrv_nextKey instance variable added to n_ds)
This service adds the Next Key class to the PFC DataStore class, n_ds. When activated, this service adds next key functionality for a specific DataStore object. Due to the nature of this object, registering of tables and columns is not available.
Example usage
// start the next key service ids_object.of_setNextKey (true) // set the next key's trans object; note: this step is not required as SQLCA is the default ids_object.inv_nextKey.of_setTransObject (ltr_object) // get next key and apply to all rows in a DataStore ids_test.inv_nextKey.of_getNextKey("Table", "Column") // get next key and apply to selected rows in a DataStore based on an expression ids_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)") // get next key and apply to all rows in a DataStore, without remembering the row status ids_test.inv_nextKey.of_getNextKey("Table", "Column", false) // get next key and apply to selected rows in a DataStore based on an expression, without remembering the row status ids_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)", false)
Export of n_cst_dwsrv_nextkey object for Datawindows
$PBExportHeader$n_cst_dwsrv_nextkey.sru $PBExportComments$Next Key DataWindow Service forward global type n_cst_dwsrv_nextkey from n_cst_dwsrv end type end forward global type n_cst_dwsrv_nextkey from n_cst_dwsrv event type long ue_insertrow ( long al_row ) end type global n_cst_dwsrv_nextkey n_cst_dwsrv_nextkey type variables protected: n_cst_nextKeyAttrib inv_nextKeyTables n_cst_nextKey inv_nextKey n_tr itr_object end variables forward prototypes public function integer of_settransobject (n_tr atr_object) public function integer of_register (string as_tablename, string as_columnname) public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression) public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_rememberrowstatus) public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_rememberrowstatus) public function integer of_getnextkey (string as_tablename, string as_columnname) public function integer of_register () end prototypes event ue_insertrow; // integer li_i integer li_numKeys long ll_nextKey string ls_tableName, ls_columnName dwItemStatus le_rowstatus // validate the next key application service is in use if not isValid(inv_nextKey) then return FAILURE // Loop thru all the tables. li_numKeys = upperBound (inv_nextKeyTables.is_tableName) For li_i = 1 to li_numKeys // get table and column names ls_tableName = inv_nextKeyTables.is_tableName[li_i] ls_columnName = inv_nextKeyTables.is_columnName[li_i] // get the next key for the table ll_nextKey = inv_nextKey.of_getNextKey(ls_tableName) if ll_nextKey = 0 then return FAILURE // set the key if idw_requestor.setItem(al_row, ls_columnName, ll_nextKey) <> 1 then return FAILURE end if next // convert row back from NewModified! to New!. le_rowstatus = idw_Requestor.GetItemStatus(al_row, 0, Primary!) If le_rowstatus = NewModified! Then idw_requestor.setItemStatus(al_row, 0, Primary!, NotModified!) end if return al_row end event public function integer of_settransobject (n_tr atr_object); // integer li_rc // validate if IsNull (atr_object) or not IsValid (atr_object) then return FAILURE // set the trans object li_rc = inv_nextKey.of_setTransObject (atr_object) // remember the trans object if li_rc = 1 then itr_object = atr_object end if return li_rc end function public function integer of_register (string as_tablename, string as_columnname); // Integer li_newUpper String ls_id // Verify required reference. If isNull(idw_requestor) Or Not isValid(idw_requestor) Then Return FAILURE // Trim the arguments. as_tableName = Trim(as_tableName) as_columnName = Trim(as_columnName) // Verify passed arguments. If isNull(as_tableName) or len(as_tableName) = 0 Then Return FAILURE If isNull(as_columnName) or len(as_columnName) = 0 Then Return FAILURE // validate column is in requestor ls_id = idw_requestor.describe (as_columnName + ".ID") if not isNumber(ls_id) then return FAILURE // Establish the boundaries of the array. li_newUpper = UpperBound (inv_nextKeyTables.is_tableName) + 1 // Set the columns. inv_nextKeyTables.is_tableName[li_newUpper] = as_tableName inv_nextKeyTables.is_columnName[li_newUpper] = as_columnName Return SUCCESS end function public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression); // // pass on event with defaults return this.of_getNextKey(as_tableName, as_columnName, as_expression, true) end function public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_rememberrowstatus); // // apply keys to a column for all rows in this datawindow long ll_row, ll_rowCount, ll_numKeys, ll_nextKey dwItemStatus le_rowStatus // get total number of rows ll_rowCount = idw_requestor.rowCount() if ll_rowCount < 1 then return ll_rowCount // number of keys = all rows ll_numKeys = ll_rowCount // get block of keys ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys) // apply keys to all rows ll_row = 1 do while ll_row > 0 and ll_row <= ll_rowCount // remember row status if ab_rememberRowStatus then le_rowStatus = idw_Requestor.getItemStatus(ll_row, 0, primary!) end if // set the column with the next key idw_requestor.setItem(ll_row, as_columnName, ll_nextKey) // reset row status to the remembered row status if ab_rememberRowStatus then // if the row status was originally new! or notModified!, then changing // it to notModified! will reset the row status. if the row status // was either newModified! or dataModified!, then the row should // already have its original row status. if le_rowStatus = new! or le_rowStatus = notModified! then idw_requestor.setItemStatus(ll_row, 0, primary!, NotModified!) end if end if ll_nextKey++ ll_row++ loop return 1 end function public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_rememberrowstatus); // // apply keys to a column for selected rows in a datawindow based // based on an expression long ll_row, ll_rowCount, ll_numKeys, ll_nextKey dwItemStatus le_rowStatus // get total number of rows ll_rowCount = idw_requestor.rowCount() if ll_rowCount < 1 then return ll_rowCount // count matching rows ll_row = idw_requestor.find(as_expression, 1, ll_rowCount) do while ll_row > 0 and ll_row <= ll_rowCount ll_numKeys++ ll_row++ ll_row = idw_requestor.find(as_expression, ll_row, ll_rowCount) loop // get out if no matches if ll_numKeys < 1 then return ll_numKeys // get block of keys ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys) // apply keys to the only matching rows ll_row = idw_requestor.find(as_expression, 1, ll_rowCount) do while ll_row > 0 and ll_row <= ll_rowCount // remember row status if ab_rememberRowStatus then le_rowStatus = idw_Requestor.getItemStatus(ll_row, 0, primary!) end if // set the column with the next key idw_requestor.setItem(ll_row, as_columnName, ll_nextKey) // reset row status to the remembered row status if ab_rememberRowStatus then // if the row status was originally new! or notModified!, then changing // it to notModified! will reset the row status. if the row status // was either newModified! or dataModified!, then the row should // already have its original row status. if le_rowStatus = new! or le_rowStatus = notModified! then idw_requestor.setItemStatus(ll_row, 0, primary!, NotModified!) end if end if ll_nextKey++ ll_row++ ll_row = idw_requestor.find(as_expression, ll_row, ll_rowCount) loop return 1 end function public function integer of_getnextkey (string as_tablename, string as_columnname); // // pass on call using defaults return this.of_getNextKey(as_tableName, as_columnName, true) end function public function integer of_register (); // // automatic registering of tables and columns with the next key service // this method looks for objects on a dataobject with a tag of "nextkey=table" n_cst_string lnv_string string ls_objects[], ls_tag, ls_tableName, ls_columnName integer li_objectCount, li_objectPointer // make sure base datawindow service is started if not isValid (idw_requestor.inv_base) then idw_requestor.of_setBase(true) end if // get dataobject objects into an array li_objectCount = idw_requestor.inv_base.of_getObjects (ls_objects[]) // loop through objects for li_objectPointer = 1 to li_objectCount // get object name ls_columnName = ls_objects[li_objectPointer] // get tag for object ls_tag = idw_requestor.describe(ls_columnName + ".tag") // get "nextkey" key. if not isNull(ls_tag) and len(trim(ls_tag)) > 0 then ls_tableName = lnv_string.of_getKeyValue (ls_tag, "nextkey", "|") end if // register table/column with the next key service if not isNull(ls_tableName) and len(trim(ls_tableName)) > 0 then this.of_register(ls_tableName, ls_columnName) end if next return 1 end function on n_cst_dwsrv_nextkey.create call super::create end on on n_cst_dwsrv_nextkey.destroy call super::destroy end on event constructor;call super::constructor; // inv_nextKey = create n_cst_nextKey end event event destructor;call super::destructor; // if isValid(inv_nextKey) then destroy(inv_nextKey) end event
Export of n_cst_dssrv_nextkey object for Datastores
$PBExportHeader$n_cst_dssrv_nextkey.sru $PBExportComments$Next Key DataWindow Service forward global type n_cst_dssrv_nextkey from n_cst_dssrv end type end forward global type n_cst_dssrv_nextkey from n_cst_dssrv end type global n_cst_dssrv_nextkey n_cst_dssrv_nextkey type variables protected: n_cst_nextKey inv_nextKey n_tr itr_object end variables forward prototypes public function integer of_settransobject (n_tr atr_object) public function integer of_getnextkey (string as_tablename, string as_columnname) public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression) public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_resetrowstatus) public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_resetrowstatus) end prototypes public function integer of_settransobject (n_tr atr_object); // integer li_rc // validate if IsNull (atr_object) or not IsValid (atr_object) then return FAILURE // set the trans object li_rc = inv_nextKey.of_setTransObject (atr_object) // remember the trans object if li_rc = 1 then itr_object = atr_object end if return li_rc end function public function integer of_getnextkey (string as_tablename, string as_columnname); // // pass on call with defaults return this.of_getNextKey(as_tableName, as_columnName, true) end function public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression); // // pass on call with defaults return this.of_getNextKey(as_tableName, as_columnName, as_expression, true) end function public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_resetrowstatus); // // apply keys to a column for selected rows in a datawindow based // based on an expression long ll_row, ll_rowCount, ll_numKeys, ll_nextKey dwItemStatus le_rowStatus // get total number of rows ll_rowCount = ids_requestor.rowCount() if ll_rowCount < 1 then return ll_rowCount // count matching rows ll_row = ids_requestor.find(as_expression, 1, ll_rowCount) do while ll_row > 0 and ll_row <= ll_rowCount ll_numKeys++ ll_row++ ll_row = ids_requestor.find(as_expression, ll_row, ll_rowCount) loop // get out if no matches if ll_numKeys < 1 then return ll_numKeys // get block of keys ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys) // apply keys to the only matching rows ll_row = ids_requestor.find(as_expression, 1, ll_rowCount) do while ll_row > 0 and ll_row <= ll_rowCount // remember row status if ab_resetRowStatus then le_rowStatus = ids_Requestor.getItemStatus(ll_row, 0, primary!) end if // set the column with the next key ids_requestor.setItem(ll_row, as_columnName, ll_nextKey) // reset row status if ab_resetRowStatus then // if the row status was originally new! or notModified!, then changing // it to notModified! will reset the row status. if the row status // was either newModified! or dataModified!, then the row should // already have its original row status. if le_rowStatus = new! or le_rowStatus = notModified! then ids_requestor.setItemStatus(ll_row, 0, primary!, NotModified!) end if end if ll_nextKey++ ll_row++ ll_row = ids_requestor.find(as_expression, ll_row, ll_rowCount) loop return 1 end function public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_resetrowstatus); // // apply keys to a column for all rows in this datawindow long ll_row, ll_rowCount, ll_numKeys, ll_nextKey dwItemStatus le_rowStatus // get total number of rows ll_rowCount = ids_requestor.rowCount() if ll_rowCount < 1 then return ll_rowCount // number of keys = all rows ll_numKeys = ll_rowCount // get block of keys ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys) // apply keys to all rows ll_row = 1 do while ll_row > 0 and ll_row <= ll_rowCount // remember row status if ab_resetRowStatus then le_rowStatus = ids_Requestor.getItemStatus(ll_row, 0, primary!) end if // set the column with the next key ids_requestor.setItem(ll_row, as_columnName, ll_nextKey) // reset row status if ab_resetRowStatus then // if the row status was originally new! or notModified!, then changing // it to notModified! will reset the row status. if the row status // was either newModified! or dataModified!, then the row should // already have its original row status. if le_rowStatus = new! or le_rowStatus = notModified! then ids_requestor.setItemStatus(ll_row, 0, primary!, NotModified!) end if end if ll_nextKey++ ll_row++ loop return 1 end function event constructor;call super::constructor; // inv_nextKey = create n_cst_nextKey end event on n_cst_dssrv_nextkey.create call super::create end on on n_cst_dssrv_nextkey.destroy call super::destroy end on event destructor;call super::destructor; // if isValid(inv_nextKey) then destroy(inv_nextKey) end event
Datawindow Object Database Information from export
table(column=(type=decimal(0) updatewhereclause=yes name=nextkey dbname="nextKey" ) procedure="1 execute dbo.spcmn_getNextKeyResultSet;1 @isTableName = :isTableName, @iiKeys = :iiKeys" arguments=(("isTableName", string),("iiKeys", number)) )
Once you have the stored procedures in your database, create a datawindow object with the spcmn_getNextKeyResultSet procedure as its datasource.
Stored Procedure for the NextKey datawindow object
This calls the spcmn_getNextKey procedure detailed in the earlier post.
/****** Object: Procedure [dbo].[spcmn_getNextKeyResultSet] Script Date: 12/30/2010 10:17:41 AM ******/ IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spcmn_getNextKeyResultSet]') AND type in (N'P', N'PC')) BEGIN DROP PROCEDURE [dbo].[spcmn_getNextKeyResultSet]; END GO SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER OFF; GO CREATE PROCEDURE [dbo].[spcmn_getNextKeyResultSet] @isTableName varchar(128) = null -- table to get next key for ,@iiKeys decimal(18,0) = null -- number of keys to get; null = 1 AS set nocount on -- ---------------------------------------------------------- -- Procedure: spcmn_getNextKeyResultSet -- Get next technical key(s) for the table name passed in. -- ---------------------------------------------------------- declare @lsERRORTEXT varchar(255) ,@liERROR int ,@liROWCOUNT int ,@liTRANCOUNT int declare @liNextKey decimal(18,0) -- work around for the PowerBuilder IDE if @iiKeys = 0 begin set @liNextKey = 0 select @liNextKey 'nextKey' return end -- initialize error handling select @liERROR = @@ERROR if @liERROR <> 0 goto ErrorHandler -- get next key exec spcmn_getNextKey @isTableName, @liNextKey output, @iiKeys select @liERROR = @@ERROR IF @liERROR <> 0 goto ErrorHandler select @liNextKey 'nextKey' return ErrorHandler: if @@TRANCOUNT > @liTRANCOUNT --0 rollback transaction select @lsERRORTEXT = isNull(@lsERRORTEXT, 'spcmn_getNextKeyResultSet: Unexpected Error Occurred!') raiserror(@lsERRORTEXT, 0, 1) WITH SETERROR GO GRANT EXECUTE ON [dbo].[spcmn_getNextKeyResultSet] TO [public]; GO
Datastore (n_ds) object export
$PBExportHeader$n_ds.sru $PBExportComments$Extension Datastore class forward global type n_ds from pfc_n_ds end type end forward global type n_ds from pfc_n_ds end type global n_ds n_ds type variables public: n_cst_dssrv_nextKey inv_nextKey end variables forward prototypes public function integer of_setnextkey (boolean ab_switch) end prototypes public function integer of_setnextkey (boolean ab_switch); //Check arguments If IsNull(ab_switch) Then Return -1 End If IF ab_Switch THEN IF IsNull(inv_nextKey) Or Not IsValid (inv_nextKey) THEN inv_nextKey = Create n_cst_dssrv_nextKey inv_nextKey.of_setRequestor ( this ) Return 1 END IF ELSE IF IsValid (inv_nextKey) THEN Destroy inv_nextKey Return 1 END IF END IF Return 0 end function on n_ds.create call super::create end on on n_ds.destroy call super::destroy end on
Datawindow (u_dw) object export
Nextkey service used in pfc_addrow and pfc_insertrow events
$PBExportHeader$u_dw.sru $PBExportComments$Extension DataWindow class forward global type u_dw from pfc_u_dw end type end forward global type u_dw from pfc_u_dw end type global u_dw u_dw type variables public: n_cst_dwsrv_nextKey inv_nextKey end variables forward prototypes public function integer of_setnextkey (boolean ab_switch) end prototypes public function integer of_setnextkey (boolean ab_switch); if IsNull(ab_switch) then return FAILURE if ab_Switch then if IsNull(inv_nextKey) or not IsValid (inv_nextKey) then inv_nextKey = Create n_cst_dwsrv_nextKey inv_nextKey.of_SetRequestor (this) return SUCCESS end if else if IsValid (inv_nextKey) then Destroy inv_nextKey return SUCCESS end if end if return NO_ACTION end function on u_dw.create call super::create end on on u_dw.destroy call super::destroy end on event destructor;call super::destructor; of_setNextKey(false) end event event pfc_addrow;// override of pfc event! ////////////////////////////////////////////////////////////////////////////// // Event: pfc_addrow // Arguments: None // Returns: long - number of the new row that was inserted // 0 = No row was added. // -1 = error // Description: Adds a new row to the end of the DW ////////////////////////////////////////////////////////////////////////////// long ll_rc boolean lb_disablelinkage // Allow for pre functionality. if this.Event pfc_preinsertrow() <= 0 then return NO_ACTION // Is Querymode enabled? if IsValid(inv_QueryMode) then lb_disablelinkage = inv_QueryMode.of_GetEnabled() if not lb_disablelinkage then // Notify that a new row is about to be added. if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (0) end if // Insert row. if IsValid (inv_RowManager) then ll_rc = inv_RowManager.event pfc_addrow () else ll_rc = this.InsertRow (0) end if // pass on event to the next key service // added here so linkage service can know about keys if isValid (inv_nextKey) then inv_nextKey.event ue_insertRow (ll_rc) if not lb_disablelinkage then // Notify that a new row has been added. if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (ll_rc) end if // Allow for post functionality. this.Post Event pfc_postinsertrow(ll_rc) return ll_rc end event event pfc_insertrow;// -- override of pfc event ////////////////////////////////////////////////////////////////////////////// // Event: pfc_insertrow // Arguments: None // Returns: long - number of the new row that was inserted // 0 = No row was added. // -1 = error // Description: Inserts a new row into the DataWindow before the current row ////////////////////////////////////////////////////////////////////////////// long ll_currow long ll_rc boolean lb_disablelinkage // Allow for pre functionality. if this.Event pfc_preinsertrow() <= PREVENT_ACTION then return NO_ACTION // Get current row ll_currow = this.GetRow() if ll_currow < 0 then ll_currow = 0 // Is Querymode enabled? if IsValid(inv_QueryMode) then lb_disablelinkage = inv_QueryMode.of_GetEnabled() if not lb_disablelinkage then // Notify that a new row is about to be added. if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (0) end if // Insert row. if IsValid (inv_RowManager) then ll_rc = inv_RowManager.event pfc_insertrow (ll_currow) else ll_rc = this.InsertRow (ll_currow) end if // pass on event to the next key service // added here so linkage service can know about keys if isValid (inv_nextKey) then inv_nextKey.event ue_insertRow (ll_rc) if not lb_disablelinkage then // Notify that a new row has been added. if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (ll_rc) end if // Allow for post functionality. this.Post Event pfc_postinsertrow(ll_rc) return ll_rc end event
Special thanks to A.J. Schroeder.