Saturday, February 25, 2012

Refresh DataSource and retain position

Refresh a DataSource is a very common task to Dynamics AX developers, as you'll most likely perform changes on a record and have to present it to the user, on a form.

The most commonly used method to refresh the DataSource to make a form show what you have changed on a record is the research method.
The only problem is that the research method doesn't retain the record that was selected, it positions the DataSource on the first record of the result, causing the user to see another record. To resolve this matter, the research method, only in Dynamics AX 2009, has a boolean parameter, retainPosition. Now if you call it by passing true to it, it will sure retain the position of the selected record after the refresh, right? Wrong...

This should only work when your form has DataSources linked by Inner Joins in it, what means that the generated query has Inner Joins in it. Having any other link types or temporary tables causes it to not retain the position, even if you pass true when calling it.

So I'll present you two ways of retaining the position of the selected record, one that works sometimes, and one that always works, although you should always try and see if it works with research(true) before trying anything else.

On the first way, you could simply get the position of the DataSource before calling research(), like this:

int position; 

position = myTable_ds.getPosition();

But does this work perfectly? Not again...

The code above should select the record that was on the previously selected record's position. What does that mean? It means that if your query result changes the record's orders, you won't have the correct record selected. This could easily happen if your form handles the records' order in a special way, or if the action is performed when the record or any records are inserted, which will certainly cause the the form to reorder the records when you tell it to refresh the DataSource.

The second way to do it will actually look for the correct record and then select it, so you can imagine it will perform a little worse than the others, but at least it will never fail (unless you, somehow, delete the record you're looking for in the meantime).

Simply copy the RecId from the record you want to keep selected to a new table buffer, like this:

MyTable      myTableHolder;
myTableHolder.RecId = myTableRecord.RecId;
myTable_ds.research(); // or: myTableRecord.dataSource().research();
myTable_ds.findRecord(myTableHolder); // or: myTableRecord.dataSource.findRecord(myTableHolder);        

We copy if to a new table buffer because the findRecord method expects a parameter of type Common. If you stored the RecId in a variable, you would have to pass it to a table buffer anyway...

And that's it, it'll first refresh the DataSource, and then look for your record and select it, so the screen might even "blink" for a second. As I said, this is not the best when thinking about performance, but at least it works...