Code highlighting

Showing posts with label FormRun. Show all posts
Showing posts with label FormRun. Show all posts

Friday, December 19, 2014

Tutorial: Auto-Complete functionality on form controls


I guess all of you are familiar with the Auto-Complete feature present in Dynamics AX. Whenever you type in a repeated value in a control on AX forms, an automatic suggestion pops up next to the entered characters and the user can choose to accept the suggestion (for example, by tabbing out of the control).
There are methods on the form (specifically, the FormRun class) that provide you with a more tight control over the auto-completion list available to the user, and this blog post will describe the behavior of these methods, as there are a few not obvious things there.

Method signature Method behavior description
getAutoCompleteString(int _elementIndex) Retrieve the auto-complete string saved under the specified index in the auto-completion list of the form.
The indexing is not control-specific, and is not very efficient at keeping the number of values in the list low, so you will see values from multiple controls here.

The method returns a container containing 2 values:
- the auto-completion string value
- the index of this string in the auto-completion list
getAutoCompleteString(int _startIndex, FormControl _control, str _searchStr) Retrieve the auto-complete string for the specified control, finding the first one in the list starting from the specified startIndex + 1.

If _searchStr is also specified, it will find the first matching entry in the list with index > startIndex. This is the closest behavior to what happens when a user types in a certain character in the entry control on the form and the kernel needs to search and populate the auto-complete value for it.

Note: There’s a small bug in this function, where it does not return the full string value for longer texts. A workaround is to use the above “overload” and retrieve the full value by index.
setAutoCompleteString(string _string, anytype _control) The first argument is clear, and that is the auto-completion string to add to the list of suggestions.

The second argument is presented as _control, but can accept either an instance of a FormControl, or an integer value of the list item index to set.

If the _string already exists in the auto-completion list for this control, nothing will happen. If it does not exist, a new item will be added to the list (number of items cannot exceed 300) with the next available index.
setAutoCompleteString(string _string, anytype _index) The auto-completion list item at index _index will be updated in the list with the specified _string value.

This method “overload” basically allows you to override a particular value in the list with a new value.
delAutoCompleteString() Deletes all auto-completion items from the list, regardless of which control they are bound to, if any.
delAutoCompleteString(anytype _index) Deletes the item with the specified index from the auto-completion list.
delAutoCompleteString(anytype _control) Deletes all auto-completion items for the specified control from the list.

When a value in one of the controls on the form is modified, this value is also automatically added to the auto-completion list. With the above methods, however, you can control, if this value should actually stay there, or be immediately removed.
This way you can, for example, restrict the user to only be provided with auto-completion hints from a certain list (similar to IntelliSense).

I created a simple form to demonstrate all the above methods in action.

Tutorial auto-complete capabilities form
Tutorial form to demo auto-completion capabilities

You can download the form from my OneDrive.


Apart from the above methods, there is also a global setting which allows you to control auto-completion list availability for a particular user. This is control on the User options screen, as highlighted in the below image:

Autocomplete in User options
User options form


If this check box is not set, auto-completion suggestions will not be shown to the user when entering data on forms, even though the auto-completion list is still maintained behind the scenes.

Thursday, May 05, 2011

Tutorial: lockWindowUpdate() vs. lock()/unlock()

There are two method pairs in X++, that are used throughout the application by everyone writing some processing on application forms. These are:

element.lock();
element.unLock();


and

element.lockWindowUpdate(true);
element.lockWindowUpdate(false);


Now, not that many people know the difference between the two methods, and only very few think about why and when should each of them be used.
I will try to describe the behavior of these methods and at the end give some recommendations on how to use them. I have done some kernel code reading (with help of kernel dev. Andy Stach, who I would like to mention here), so what I write below is more or less backed up by code.
If you disagree with some of the recommendations though, please share your experience in using these methods through comments for this post.

FormRun.lockWindowUpdate()

is basically a wrapper around the LockWindowUpdate Win32 function. What it does is pretty simple:
When a window is locked, all attempt to draw into it or its children fail. Instead of drawing, the window manager remembers which parts of the window the application tried to draw into, and when the window is unlocked, those areas are invalidated so that the application gets another WM_PAINT message, thereby bringing the screen contents back in sync with what the application believed to be on the screen.
See the link on MSDN for a detailed description.
Note, that according to MSDN, it should not be used for general purpose suppression of redraw operations, but only when dealing with drag&drop operations. This does not hold true for AX, where this method is used all over the place to prevent redraw of controls on the form.
Another interesting point is that only one window can be locked at the same time. So, any nested calls to lockWindowUpdate will be ignored, but when unlocking, only the outer-most unlock will actually invoke the Win32 counterpart. Now, I have not seen this used in X++, which is for the better.

FormRun.lock()

is internally invoking lockWindowUpdate to prevent the redraw of the window, and then also prevents the IntelliMorph control layout engine from running. This is commonly used in scenarios where control properties affecting control arrangement are being set in a loop, which provides a performance optimization as it avoids redundant arrange calls being processed. On the other hand, when calling FormRun.unlock, more work will need to be done, compared to using lockWindowUpdate(false), where the control layout changes were actually processed by the layout engine, but simply not displayed.

So, based on my investigation, I would suggest to use the following recommendations when doing form development:

  • When formRun.resetSize() is used, specifically, when some controls become visible, increasing the form size, always use formRun.lock()/unlock(), otherwise the change in the size of the form might not get reflected on the screen correctly.
  • When changing multiple layout properties (Left, Width, etc.) on one or more controls, use lock/unlock
  • When you modify the properties that do not impact the layout of controls on the form, use formRun.lockWindowUpdate(), or, if there are only very few control properties being modified, do not lock the form window at all.