When navigating between forms that do not have the same keys or you need to act on the transferred data you need to customize the data transfer. For forms that share the same keys, framework functionality will automatically populate the receiving form.
The standard way to handle this in the sending form is to override the
vrtDataSourcePrepareKeyTransfer
method.
The following example shows how to navigate from ifsapf:frmPurchasePart to ifsapf:frmSite. They both share one of the keys CONTRACT, but ifsapf:frmPurchasePart also has the key PART_NO, so we shouldn't send that.
public override SalNumber vrtDataSourcePrepareKeyTransfer(SalString sWindowName) { if (sWindowName == Pal.GetActiveInstanceName("frmSite")) { SalArray<SalString> sItemNames = new SalArray<SalString>(); SalArray<SalWindowHandle> hWndItems = new SalArray<SalWindowHandle>(); sItemNames[0] = "CONTRACT"; hWndItems[0] = cDataFieldContract; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init(this.p_sLogicalUnit, this, sItemNames, hWndItems); Ifs.Fnd.ApplicationForms.Var.DataTransfer.TypeSet("CREATE_WINDOW"); return 0; } return ((cDataSource)this).DataSourcePrepareKeyTransfer(sWindowName); }
Example: Navigating from ifsapf:frmPurchasePart to ifsapf:frmSite
If you override this method it will be called for all data transfers. Your
method needs to handle all navigations that transform keys and also handle the
default navigations. This is done in the else clause in the example, (cDataSource
) should be replaced with the base class of the current form).
If the data that should be transferred isn't located in a field, i.e. it needs to be calculated in some way, you need to use a slightly different approach. The following (degenerate) example shows how to navigate from ifsapf:frmPurchasePart to ifsapf:frmCustomerInfo, with a "calculated" value for the key CUSTOMER_NO.
... else if (sWindowName == Pal.GetActiveInstanceName("frmCustomerInfo")) { SalArray<SalString> sItemNames = new SalArray<SalString>(); sItemNames[0] = "1000"; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Reset(); Ifs.Fnd.ApplicationForms.Var.DataTransfer.SourceNameSet(Ifs.Fnd.ApplicationForms.Int.QualifiedItemNameGet(this)); Ifs.Fnd.ApplicationForms.Var.DataTransfer.ItemAdd("CUSTOMER_NO", sItemNames); return Ifs.Fnd.ApplicationForms.Var.DataTransfer.TypeSet("CREATE_WINDOW"); } ...
Example: Navigation to ifsapf:frmCustomerInfo
You can also combine these techniques by first using
Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init
and then add more
parameters with Ifs.Fnd.ApplicationForms.Var.DataTransfer.ItemAdd
.
Note: To simplify the examples the menu handling invoking the data transfers are left out, see Create Standard Data Transfer.
If you do not use the Ifs.Fnd.ApplicationForms.Const.PM_DataSourceCreateWindow
message to invoke the navigation, vrtDataSourcePrepareKeyTransfer
will not be called. Then you need to
create
the customized data transfer before calling SessionNavigate
, as in the example below.
... case Ifs.Fnd.ApplicationForms.Const.METHOD_Execute: sArray[0] = "COMPANY"; sArray[1] = "INVOICE_ID"; hWndArray[0] = dfsCompany; hWndArray[1] = dfnCorrectionInvoiceId; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init(this.p_sLogicalUnit, this, sArray, hWndArray); SessionNavigate(Pal.GetActiveInstanceName("tbwOverviewCustomerInvoice")); return 1; ...
Example: Part of the menu-handler UM_ViewCorrInvoice
in
ifsapf:frmChangeCustomerInvoice.
SessionNavigate
can also be used to customize
the DataItemZoom-functionality, as in the
example below. (You also need to handle the
PM_DataItemZoom message.)
private void dfsDeliveryAddress_OnPM_DataItemZoom(object sender, WindowActionsEventArgs e) { e.Handled = true; if (Sys.wParam == Ifs.Fnd.ApplicationForms.Const.METHOD_Execute) { this.sItemNames[0] = "COMPANY"; this.hWndItems[0] = this.dfsCompany; this.sItemNames[1] = "ADDRESS_ID"; this.hWndItems[1] = this.dfsDeliveryAddress; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init(this.p_sLogicalUnit, this, this.sItemNames, this.hWndItems); SessionNavigate(Pal.GetActiveInstanceName("frmCompany")); e.Return = true; return; } e.Return = Sal.SendClassMessage(Ifs.Fnd.ApplicationForms.Const.PM_DataItemZoom, Sys.wParam, Sys.lParam); }
Example: Customized DataItemZoom
for the Delivery Address field in
ifsapf:frmSite.
If you need to transfer other data than keys you need to use the
SessionNavigateWithParam
method. The parameter(s)
added this way need to be handled explicitly in the
receiving form. The following
example shows how to pass parameters from a sending form.The standard method
DataSourcePrepareKeyTransfer
is called to initiate a standard transfer. A
parameter is then added and the navigation is done by calling
SessionNavigateWithParams
.
... SalString sWindowName = Pal.GetActiveInstanceName("frmCustomerOrder"); cMessage Params = new cMessage(); ... DataSourcePrepareKeyTransfer(sWindowName); Params.AddAttribute("SELECT_CHARGES_TAB", "TRUE"); SessionNavigateWithParams(sWindowName, Params); return true; ...
Example: From the method CreateCustomerOrderForm
in ifsapf:frmShipDiff.
You can also use Data Transfer to transfer data to and from a modal dialog. The following example shows how to send data to a modal dialog.
... sItemNames[0] = "STREET"; sItemNames[1] = "HOUSE_NO"; sItemNames[2] = "FLAT_NO"; sItemNames[3] = "COMMUNITY"; sItemNames[4] = "DISTRICT"; hWndItems[0] = dfsStreet; hWndItems[1] = dfsHouseNo; hWndItems[2] = dfsFlatNo; hWndItems[3] = dfsCommunity; hWndItems[4] = dfsDistrict; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init("NewEmpWizard", this, sItemNames, hWndItems); if (dlgDetailedPersonAddress.ModalDialog(this)) { ... } ...
Example: Ifs.Application.Person.dlgEmployee.is initiating the transfer and Ifs.Application.Enterp.dlgDetailedPersonAddress receives the data.
If the sending form has arranged the transferred data to
correctly match the keys of the receiving form you do not have to do anything if
you only want the form to populate with the transferred data. However, if the data is arranged in another way or you
want to
act on the received data you need to override the vrtActivate
method in the receiving form.
The example below shows how to implement different behavior depending on the
sending source. The source name is retrieved by calling
Ifs.Fnd.ApplicationForms.Var.DataTransfer.SourceNameGet
.
The example shows that if the sending source is "ContractCustomer" then a custom
where statement is constructed and used to populate the form.
public override SalNumber vrtActivate(fcURL URL) { if (Ifs.Fnd.ApplicationForms.Var.DataTransfer.SourceNameGet() == "ContractCustomer") { Ifs.Fnd.ApplicationForms.Var.DataTransfer.ItemGet("CONTRACT_NO", sContractNo); Ifs.Fnd.ApplicationForms.Var.DataTransfer.ItemGet("CUSTOMER_LINE_NO", sCustomerLineNo); sWhere = "CONTRACT_NO = :i_hWndParent.frmContractCustomer.sContractNo[0] AND CUSTOMER_LINE_NO = :i_hWndParent.frmContractCustomer.sCustomerLineNo[0]"; tblContractCustomer.vrtDataSourceUserWhere(Ifs.Fnd.ApplicationForms.Const.METHOD_Execute, sWhere.ToHandle()); tblContractCustomer.vrtDataSourcePopulate(Ifs.Fnd.ApplicationForms.Const.METHOD_Execute, 0); Ifs.Fnd.ApplicationForms.Var.DataTransfer.Reset(); } return base.Activate(URL); }
Example: In ifsapf:frmContractCustomer the first values for CONTRACT_NO and CUSTOMER_LINE_NO are used to alter the where-statement.
Note: The framework default implementation of the
activate method will automatically try to format a where statement and populate
the form if a data transfer is available. This means that if the data transfer
is used for custom behavior it is important to call DataTransfer.Reset
before calling
base.Activate
.
If you need to completely override the default data transfer,
vrtActivate
should return without calling
base.Activate.
If you need to transfer other data than keys you need to use the
SessionNavigateWithParam
method, which is
explained above. This example
shows how to use URL.iParameters.FindAttribute
to extract the custom parameters:
// Parameters passed along with SessionNavigateWithParams(). sTriggerMessage = URL.iParameters.FindAttribute("TRIGGER:PAM_NewForCustomer", "FALSE"); sCustomerId = URL.iParameters.FindAttribute("CUSTOMER_ID", "");
Example: From vrtActivate
in
ifsapf:frmCustomerOrder, sent by the example
above.
If the data transfer object is used to send data to and from a dialog, receiving data is done by overriding the vrtActivate method and there extract the data from the data transfer object, as described above.
To return data to the calling form the data transfer object is again to be initialized with the returning information as shown in the example below. The data must then be extracted by the form when the dialog returns.
... sItemNames[0] = "STREET"; sItemNames[1] = "HOUSE_NO"; sItemNames[2] = "FLAT_NO"; sItemNames[3] = "COMMUNITY"; sItemNames[4] = "DISTRICT"; hWndItems[0] = dfsStreet; hWndItems[1] = dfsHouseNo; hWndItems[2] = dfsFlatNo; hWndItems[3] = dfsCommunity; hWndItems[4] = dfsDistrict; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init("NewEmpWizard", this, sItemNames, hWndItems); this.EndDialog(Sys.IDOK); ...
Example: Initialization of the data transfer object before the dialog is closed.
The DataTransfer design results often in a lot of "overhead" code, where you need to define array parameters, connect SQL columns and items to these, initializing the the actual DataTransfer and finally ending the whole procedure by navigating to the target window. This pattern is re-used over and over again for why a simplified API is available doing all these steps without all that "overhead" code.
Following example shows a custom Zoom implementation, using a standard DataTransfer design pattern, navigating from ifsapf:frmOrderQuotation to ifsapf:frmCustomerInfo and transferring the key CUSTOMER_ID.
private void dfsCustomerNo_OnPM_DataItemZoom(object sender, WindowActionsEventArgs e) { e.Handled = true; if (Sys.wParam == Ifs.Fnd.ApplicationForms.Const.METHOD_Inquire) { e.Return = Ifs.Fnd.ApplicationForms.Var.Component.IsWindowAvailable(Pal.GetActiveInstanceName("frmCustomerInfo")) && Ifs.Fnd.ApplicationForms.Var.Security.IsDataSourceAvailable(Pal.GetActiveInstanceName("frmCustomerInfo")); return; } if (Sys.wParam == Ifs.Fnd.ApplicationForms.Const.METHOD_Execute) { sItemNames = new SalArray<SalString>(); hWndItems = new SalArray<SalWindowHandle>(); this.sItemNames.SetUpperBound(1, -1); this.hWndItems.SetUpperBound(1, -1); this.sItemNames[0] = "CUSTOMER_ID"; this.hWndItems[0] = this.dfsCustomerNo; Ifs.Fnd.ApplicationForms.Var.DataTransfer.Init("CUSTOMER_INFO", this, this.sItemNames, this.hWndItems); SessionNavigate(Pal.GetActiveInstanceName("frmCustomerInfo")); e.Return = true; } }
Example: Zooming from ifsapf:frmOrderQuotation to ifsapf:frmCustomerInfo using standard DataTransfer.
Same implementation can be redesigned using a simplified DataTransfer design
patter, resulting in less code.
For that, data source objects like cFormWindow, cTableWindow, cDialog etc,
provide the methods DataTransferTo & CanTransferTo.
Used as the "Executing" method where you specify the target window (the one to transfer to), and the item(s) to include in the transfer using a suite of overloaded methods. Each item that should be transferred can be defined by specifying:
this.DataTransferTo(Pal.GetActiveInstanceName("tbwCustomerOrderLine"), "ORDER_NO", "LINE_NO", "REL_NO");
this.DataTransferTo(Pal.GetActiveInstanceName("tbwCustomerOrderLine"), this.colsOrderNo, this.colnLineNo, this.colnRelNo);
IDictionary<string, SalWindowHandle> keys = new Dictionary<string, SalWindowHandle>(); keys.Add("ORDER_NO", this.colsOrderNo); keys.Add("LINE_NO", this.colnLineNo); keys.Add("REL_NO", this.colnRelNo); this.DataTransferTo(Pal.GetActiveInstanceName("tbwCustomerOrderLine"), keys);
The overload method that uses the IDictionary<string, SalWindowHandle> may be convenient when the SQL Column names differs in the source window (e.g. "CUSTOMER_ID") and the target window (e.g. "CUSTOMER"). The method uses the SalWindowHandle defining the source object and the string value defining the target SQL Column name.
Used as the "Inquire" method where you specify the target window. Will result in TRUE if the target window is available.
return this.CanDataTransferTo(Pal.GetActiveInstanceName("tbwCustomerOrderLine"));
So, the same custom Zoom implementation shown earlier, being redesigned using a simplified DataTransfer design pattern could look like:
private void dfsCustomerNo_OnPM_DataItemZoom(object sender, WindowActionsEventArgs e) { e.Handled = true; if (Sys.wParam == Ifs.Fnd.ApplicationForms.Const.METHOD_Inquire) { e.Return = this.CanDataTransferTo(Pal.GetActiveInstanceName("frmCustomerInfo")); } else if (Sys.wParam == Ifs.Fnd.ApplicationForms.Const.METHOD_Execute) { e.Return = this.DataTransferTo(Pal.GetActiveInstanceName("frmCustomerInfo"), "CUSTOMER_ID"); } }
Example: Zooming from ifsapf:frmOrderQuotation to ifsapf:frmCustomerInfo using simplified DataTransfer.