Maintaining the selected row after sorting a DataGridView

Hi,

Simple question, hopefully there's a simple answer:

How can I maintain the same selected row even after a sort on a DataGridView

In other words, after new items have been added to the binding source and a sort has been forcefully been carried out (in the custom BindingList), how can I ensure that the selected row is the same row as the one selected before the sort

Bear in mind, I'm doing the sorting in a custom BindingList so I can't just handle the Sorted or SortCompare events and implement my own logic there.

Thanks,

Jiten



Answer this question

Maintaining the selected row after sorting a DataGridView

  • Reed Robison

    That works perfectly. Thanks very much!

  • tamccann

    You will have to record some info that can uniquely identify the row (primary key ) in the ColumnHeaderMouseDown event and then after the sort is done find the row. You'll want to check for the DataBindingComplete event with the ListChangeType of Reset to know when the sort is completed.
    -mark
    DataGridView Program Manager
    Microsoft
    This post is provided "as-is"

  • Jimmy Bogard

    Seems as though the SelectedRows is not being updated in time for the event.
    I'll get someone to investigate this possibility and report back.
    how do I make the row I select programmatically to be in focus (and visible without scrolling) in the DataGridView
    Set the CurrentCell property causes the grid to scroll that cell into view.

    -mark
    DataGridView Program Manager
    Microsoft
    This post is provided "as-is"


  • Thomas Roethlisberger

    That looks good.

    After this, it is the same previous row that is always coming up as the currently selected row
    I'm not sure I follow what is going on here. Can you explain this a bit more Also note you don't want to store off the DataGridViewRow, but some data in the row (like a field in the row that corresponds to the primary key).

    -mark
    DataGridView Program Manager
    Microsoft
    This post is provided "as-is"


  • MKB

    this.itemsDataGridView.CurrentCell = rowToSelect.Cells[0];

    This still doesn't make the DataGridView automatically scroll to the selected row and bring it into view :(


  • Jean-Marc Flamand dit le_beluet

    There are two issues.

    First, I realized that the ColumnHeaderMouseClick event is too late -- the grid has already sorted the column, so you'll need to use an event prior to the click -- CellMouseDown. You can check the RowIndex being -1 for mouse down actions on the column header cells. You just need to keep track of what rows are selected.

    The next issue is when handling the DataBindingComplete event -- while you can set a row as being selected with no problems, you probably really want to set the current cell as well. Setting the current cell in the DataBindingComplete event is ignored, so we have to set it after the event fires. The easiest way I know to do that is to use C# anonymous delegates and "post" the work that I want to do so that it will occur after the DataBindingComplete event.

    Here is my code for my CellMouseDown and DataBindingComplete event. I'm assuming that only one row is selected. Also note that there is a big difference between a row being selected and keeping track of what current cell is, unless you have full row selection enabled.


    private string customerID;
    private void customersDataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
    {
    if (!String.IsNullOrEmpty(customerID) && e.ListChangedType == ListChangedType.Reset)
    {
    int row = customersBindingSource.Find("Customer ID", customerID);
    customersDataGridView.BeginInvoke((MethodInvoker)delegate()
    {
    customersDataGridView.Rows[row].Selected = true;
    customersDataGridView.CurrentCell = customersDataGridView[0, row];
    });
    }
    }

    private void customersDataGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
    {
    if (e.RowIndex == -1)
    {
    customerID = customersDataGridView.SelectedRows[0].Cells["customerIDColumn"].Value.ToString();
    }
    }

    -mark
    DataGridView Program Manager
    Microsoft
    This post is provided "as-is"


  • bener

    Hi Mark,

    After stepping through the debugger (with checkpoints on SelectionChanged and DataBindingComplete event handlers) i've noticed that the SelectionChanged handler gets called twice BEFORE the DataBindingComplete (when my underlying BindingList calls OnListChanged). This means that the old selected row value is being lost.

    Also, i've checked that the Find method is looking up the value in the new list and this works fine.

    Thanks,

    Jiten


  • Max Komarov

    Do you have your custom bindinglist available where you can send it to me so I can try this out also I'm curious specifically on the SelectionChanged being fired twice. Look in the debugger and check the dgv.SelectedRows.Count property. When the grid receives a reset it actually removes all rows and then rebinds to the "new" list, so there might be an extra SelectionChanged event but the count of rows selected would be 0.

    -mark

    DataGridView Program Manager

    Microsoft

    This post is provided "as-is"


  • Destroy89

    Thanks very much.

    Here's some more information on my databinding implementation (just in case):

    CustomSortableBindingList => BindingSource.DataSource => DataGridView.DataSource

    Cheers,

    Jiten


  • stebe000

    Thanks for the suggestion Mark. Unfortunately it isn't working 100%.

    I've had to use the ColumnHeaderMouseClick event because I couldnt find the ColumnHeaderMouseDown.

    Seems as though it only works the first time a column is clicked for a sort. After this, it is the same previous row that is always coming up as the currently selected row (I've stepped through the debugger to verify this).

    Here are the exact steps i'm following:

    1. Create a local variable to store the Item ID.

    2. Handle the ColumnHeaderMouseClick event:

    2.1. Check that the Left mouse button was clicked.

    2.2. Assign the Item ID value of the currently selected row [1] to the local variable.

    3. Handle the DataBindingComplete event:

    3.1. Check that the ListChangeType is Reset.

    3.2. If the local variable has not been set then set it now (this is for when the DataGridView is first populated). If the local variable is set then search through all the rows and select the one that corresponds to it.

    [1] - I get the currently selected row with the following code:

    DataGridViewRow row = null;

    if (this.itemsDataGridView.SelectedRows.Count > 0)

    row = this.itemsDataGridView.SelectedRows[0];

    How does this sound to you

    Also, on a similar note, I need to be able to maintain the selected row even after new rows have been added through the custom BindingList (note: I do a manual merge of items and then force a sort and OnListChanged [with ListChangedType.Reset] all within the custom BindingList). Any thoughts on how i coudl do this


  • Alex1123123

    Hi Mark,

    I am indeed storing a unique string value from the row's binded data.

    After this, it is the same previous row that is always coming up as the currently selected row

    In step 3.2, i mentioned that if the local variable has not been set then it is set for the first time there. Problem is, in step 2.2 the row always seems to return the same string ID as in step 3.2. This means that the first row is always the one being selected. Seems as though the SelectedRows is not being updated in time for the event. ( )

    On a side note, how do I make the row I select programmatically to be in focus (and visible without scrolling) in the DataGridView

    Thanks,

    Jiten


  • kunalmukherjii

    Hi Mark,

    I'm stil having problems sorting out these issues.

    I'd appreciate any further help.

    Thanks,

    Jiten


  • Jesse Cheng

    Hi Mark,

    I've emailed you the custom binding list implementation.

    You're right about the extra SelectionChanged events, where the count of the rows selected is 0. In total, the event fires 3 times when a reset occurs.

    Also, when the DGV first loads up, and is bound to, SelectionChanged fires 7 (!) times. Mind you, I do re-initialise the bindingsource at runtime when it first loads so this causes some additional events to be fired.

    Thanks,

    Jiten


  • John_C

    I'm not sure I know where the problem is. I tried something similar -- In the SelectionChanged event I remember the customer ID like so:



    private void customersDataGridView_SelectionChanged(object sender, EventArgs e)
    {
    if (customersDataGridView.SelectedRows.Count > 0)
    {
    customerID = customersDataGridView.SelectedRows[0].Cells["customerIDColumn"].Value.ToString();
    }
    }

    I use the same DataBindingCompleted code as above. Next I put a button on my form that calls resetbindings and selection/focus remains correct. Can you debug your code a bit and ensure you are getting the correct "id" value. Also ensure that in your "Find" method you lookup the value using the new list.

    -mark
    DataGridView Program Manager
    Microsoft
    This post is provided "as-is"


  • Maintaining the selected row after sorting a DataGridView