Code highlighting

Thursday, January 28, 2010

Lookup form returning more than one value

I have been posted with the question of creating a lookup that would return more than 1 value into the calling record, filling in a number of fields and populating the selected control with the corresponding value at the same time.

I will try to answer that question by an example from the standard application, particularly using one of the forms owned by my (Inventory) team.

Example


If we take a look at the lookup form for one of the item inventory dimensions (Configuration, Size or Color), we'll see the following tab page on it:
(assuming more than 1 item dimension is enabled for this item)



Basically, the user is able to select the configuration that belongs to a specific dimension combination. Note the check-box "" under the grid. If the user sets that check-box and then selects the configuration from a combination, the values for all item dimensions will be copied to the record from the parent form.
So, basically, the lookup returns more than just the configuration dimension value, but also size and color.

How is that implemented?


The lookup is implemented using a custom form, present in AOT, with name ConfigIdLookup.

The methods important for a working lookup are:

  • init() method.

In particular, the way the calling control is determined based on the args passed into the form:


callerControl = SysTableLookup::getCallerStringControl(element.args());

  • selectMode() method.

(called from setSelectMode() on this form). This method accepts an AX form control as parameter, and informs the kernel about which control should be used as the returning control.

  • closeSelect() method.

This method is executed on lookup forms wheneven a selection of a particular value is made. So, obviously, in this method you still have access you all the fields of the datasource with the selected record, as well as to the calling form through element.args(). Here is the code that handles the update of multiple fields:


if (ctrlTabPageCombination.visible() &&
selectAllCombi &&
ctrlTabPageCombination.isActivePage())
{
// Genericly determine the InventDim datasource on the calling form
callerInventDimDS = inventDimFormSetup.callerInventDimFormDatasource();
if (callerInventDimDS)
{
//...
// Get the current record of that datasource
callerInventDim = callerInventDimDS.cursor();
// Set the needed fields on the record based on the selection in the lookup form
// Basically, this is how you can achieve returning more than one value from a lookup
// Get the record where the values need to be put in, and put them in from the currently selected record
if (inventDimCombination_ConfigId.visible() && inventDimCombination.ConfigId)
callerInventDim.ConfigId = inventDimCombination.ConfigId;
if (inventDimCombination_InventSizeId.visible() && inventDimCombination.InventSizeId)
callerInventDim.InventSizeId = inventDimCombination.InventSizeId;
if (inventDimCombination_InventColorId.visible() && inventDimCombination.InventColorId)
callerInventDim.InventColorId = inventDimCombination.InventColorId;
// Refresh the datasource so that values are displayed on the calling form
callerInventDimDS.refresh();
}
}



If you want to know more


  • Spend some time investigating the existing methods on SysTableLookup class, like the method getCallerStringControl, filterLookupPreRun_DS, filterLookupPostRun.

  • Look at the methods in InventDimCtrl_Frm_Lookup class. They handle all the logic of looking up inventory dimensions, and contain some nice examples of traversing the calling object.

  • Investigate, how the lookup form described above prevents the closing of the form when the check-boxes are clicked or tab pages changed. Hint: take a look at the usage of canSelect form variable

Tuesday, January 26, 2010

Short posts about AX every day

OK, so here is another one of those "A new blogger in town" posts.

But I couldn't resist, since I really like the idea of posting some little useful things about AX on a daily basis (weekends excluded, thank God!)

So I suggest all of you go and check it out for yourself.

The link is:
http://axdaily.blogspot.com/

Thursday, January 14, 2010

Run AX as a different user from Windows Explorer

This is something useful for people who have already switched to Windows Server 2008 or Windows Vista / Windows 7.
As you have probably noticed, the Run as... command is not available in these versions of Windows any longer (it's still available from command line, of course, but that's not as user friendly).

As AX users/developers/etc., we all ofter have to log into AX as a different user, for example to verify security settings for a particular AX role being setup.
Having Run as... command in the context menu really saves time here.

So, Microsoft, namely Mark Russinovich, provided a way to return this useful command back into the standard context menu.

You can download and install ShellRunas from technet.

Now, to install it, simply follow the easy instructions below:
  1. Download and unzip ShellRunas by following the link above
  2. Copy ShellRunas.exe to your Windows\System32 folder
  3. Open a Command Line and run the following command: shellrunas /reg
  4. A message box confirming successfull installation should pop up. Click OK
  5. Now, holding down the SHIFT key, right-click the AX icon. You will see a Run as different user... item in the context menu


To uninstall the Shellrunas utility, simply execute the following command from a command prompt: shellrunas /unreg

Tuesday, January 05, 2010

Editor script for simplifying the search in AOT

When doing code refactoring, or when investigating a particular class or form to understand the functionality behind it, it is often handy to search for variables used in its methods, looking at where and how each variable is used.

It is tedious to manually go through the steps of copying the name of the variable into the clipboard, opening up the parent class/form, opening up the AOT Find dialog for it, pasting the variable name into the Selected Text field and pressing Find now.

I have automated these steps, putting the code into an editor script.

You can download the xpo, which contains the EditorScripts class with the additional method, by following this link.

Now you should be able to very easily and quickly find the required code.

The code of the editor script:

///
/// Editor script for finding all occurrences of the selected text in the parent node of the selected method
///

///
/// Reference to the AX editor
///
///
/// 2010-01-05, ivanv
///

void addIns_FindAllOccurrencesInNode(Editor e)
{
#AOT
TreeNode treeNode;
FreeText selectedText;

FreeText GetSelectedTextFromEditor(Editor _e)
{
str 1 curSymbol;
int iCopyFrom;
int iCopyTo;
FreeText _selectedLine;
;
if (_e.markMode() != MarkMode::NoMark && _e.selectionStartCol() != _e.selectionEndCol())
{
_selectedLine = strLRTrim(subStr(_e.currentLine(), _e.selectionStartCol(), _e.selectionEndCol() - _e.selectionStartCol()));
}
else
{
_selectedLine = _e.currentLine();
for (iCopyFrom = _e.columnNo()+1; iCopyFrom >= 0; iCopyFrom--)
{
curSymbol = subStr(_selectedLine, iCopyFrom, 1);
if (!strAlpha(curSymbol) && curSymbol != '_')
break;
}
for (iCopyTo = _e.columnNo()+1; iCopyTo <= strLen(_selectedLine); iCopyTo++)
{
curSymbol = subStr(_selectedLine, iCopyTo, 1);
if (!strAlpha(curSymbol) && curSymbol != '_')
break;
}
_selectedLine = (iCopyFrom < iCopyTo) ? subStr(_selectedLine, iCopyFrom + 1, iCopyTo - iCopyFrom - 1) : '';
}
return _selectedLine;
}

void FindLinesContainingSelectedText(FreeText _selectedLine)
{
Args args = new Args(formstr(SysAOTFind));
FormRun sysAOTFindFormRun;
FormStringControl containingTextCtrl;
FormButtonControl findNowBtnCtrl;
;

sysAOTFindFormRun = classFactory.formRunClass(args);
sysAOTFindFormRun.init();
sysAOTFindFormRun.run();

// Set the text to find
containingTextCtrl = sysAOTFindFormRun.design().controlName(identifierStr(ContainingText));
containingTextCtrl.setFocus();
containingTextCtrl.pasteText(_selectedLine);

sysAOTFindFormRun.detach();

// Launch the search process
findNowBtnCtrl = sysAOTFindFormRun.design().controlName(identifierStr(FindNow));
findNowBtnCtrl.clicked();
}
;

selectedText = GetSelectedTextFromEditor(e);
if (selectedText)
{
// Find the currently open method node
treeNode = TreeNode::findNode(e.path());
// Find the parrent node of the method
treeNode = TreeNode::findNode(xUtilElements::getNodePathRough(xUtilElements::parentElement(xUtilElements::findTreeNode(treeNode))));
if (treeNode)
{
treeNode.AOTnewWindow();

FindLinesContainingSelectedText(selectedText);
}
}
}