Hello,
We are using Infragistics Version 10.3.20103.1000 Ultragrid on one of the forms in our project. In the CellChange event there is a call to our custom LoadGrid method. This method resets the datasource to the latest version of data retrieved from the database. This causes the focus to go back to the first cell in the first row. I expect this behavior, but our users are requesting this be changed.
The requirement is that whatever the grid looks like when the user changes the cell, they want the screen to look like that after the grid is reloaded with fresh data. Is this even possible?
I have tried a few ideas, but none have worked exactly the way we hoped. The best I have come up with is to get the grid.ActiveRow.Index value at the beginning of the CellChange event and assign this value to a variable called "activeIndex". Then after the grid is reloaded, I use the activeIndex value to access the row I want and then I know which cell I want the focus to be on, so I use the following two lines of code in an attempt to get the grid to go back to where it was before the CellChange event.
gridInvoice.Rows(activeIndex).Cells("RebuttalStatusID").Activate() gridInvoice.Rows(activeIndex).Cells("RebuttalStatusID").Selected = True
Unfortunately, this doesn't do exactly what we want. I have attached a video showcasing the behavior. I understand the screen will jump or the users will notice some movement from when the grid reloads and to when I activate and select the cell, but is there a way to make the scroll bars and appearance of the grid go back to the exact way it was the moment the user clicks the dropdown?
Thank you in advance!
This seemed like kind've a fun and interesting challenge, and it's a pretty common question, so I whipped up a quick sample.
The sample has an abstract class called StoredGridState. This class basically handles the ActiveRow, ActiveCell, and FirstRow positioning. But it's data-agnostic. It doesn't know anything about the data or how to identify a row or find that row by an identifier. So you could derive a class from this one and implement the two abstract methods:
GetRowIndentifier - returns a unique identifier for a row.
FindRowByIndentifier - finds a row (if there is one) from a unique id.
The sample also contains a sample derived class called PrimaryKeyStoredGridState. This is for cases where you have a simple, single primary key field that you can use to identify a row. It implements the two abstract methods based on that primary key field and voila.
Let me know if you have any questions.
WinGrid_SaveAndRestoreActiveRow.zip
Hi Scott,
If you assign a new DataSource to the grid, or your DataSource sends a Reset notification, then the grid has no choice by to throw away all of the rows and re-bind to the new DataSource. There is no way for the grid to match up the old rows with the new rows in the data source.
So, in general, the best thing to do is - don't do that. If you can achieve what you need by changing the existing data source instead of Resetting or creating a new one, that's generally the best way to go.
If that's not an option for you, then there are a number of issues you will need to deal with in order to save and restore the state of the grid.
First, you will want to store the ActiveRow/Cell. It seems that you are already on your way down this path, storing the Index of the active row. But that might not be reliable. Can you assume that the active row will always be in the same position? Is is possible that the new DataSource you are using will have other rows before the active row? Or that some of the rows above the active row will not be there? If so, the index of the row will have changed. In fact... can you even be certain that the ActiveRow still exists in the new data source at all?
If you can make those assumptions, then it once again begs the question of why you are resetting your data source. But it also means that you can use the index of the row and that will be reliable. If not, then you would be better off storing some better information about the row in order to identify it - like the Primary Key, for example. If you do that, then when you go to restore the grid's state, you would have to loop through the rows in order to find the matching row. This is a better approach in a lot of ways, because it's more reliable and you could handle the case where the row no longer exists.
Once you get over that hurdle, I think you probably want to activate the cell (like you are doing) and then use grid.PerformAction(EnterEditMode) to put the cell into edit mode, rather than selecting it.
The next issue is the grid's scroll position. Activating the row will bring it into view, but the row could be anywhere within the visible area, so it's unlikely that it will end up in exactly the same scroll position. Here again, we run into the same sort've problem - if the data source has changed, it may not be possible to scroll to the same position. If there have been any additions or deletions above the active row, then scrolling to the same position might not be possible. But if the rows are the same, then you can store the first visible row and then restore it just as you do with the ActiveRow. Again, you will want to use some primary key information to identify the row. The only difference in this case is that you will use grid.ActiveRowScrollRegion.FirstRow instead of grid.ActiveRow.
Finally, there's likely to be some flickering while you reset the data source, scroll the grid, and re-enter edit mode on the cell. But you can minimize this by surrounding the code with BeginUpdate and EndUpdate to prevent the grid from painting.
So the basic process would look something like this:
grid.BeginUpdate();
try
{
// store the primary key or some other unique identifier for the FristRow
// store the primary key or some other unique identifier for the ActiveRow
// Reset the grid's DataSource
// Restore the FirstRow
// Restore the ActiveRow and put the grid into edit mode. Actually... I'm not entirely sure that you will be able to EnterEditMode inside a BeginUpdate/EndUpdate block. You might have to call PerformAction AFTER the EndUpdate.
}
finally
grid.EndUpdate();