Create Custom Data Transfer

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.

Contents

Sending Form

The standard way to handle this in the sending form is to override the vrtDataSourcePrepareKeyTransfer method.

Overriding vrtDataSourcePrepareKeyTransfer

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.

SessionNavigate

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.

Zoom

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.

SessionNavigateWithParam

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.

ModalDialog

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.

Receiving Form

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.

SessionNavigateWithParam

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.

ModalDialog

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.

Simplified DataTransfer

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.

DataTransferTo

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:

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.

CanTransferTo

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"));

Simplifying

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.