Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selected rows when sorting DataGridView in WinForm application

In a WinForm application, C# 4.0, I have a DataGridView bound to a SortableBindingList. Hence it can be sorted by clicking on the header column - all fine so far ;-)

The problem is, that selected rows seem to be "remembered" by the row number. Here is what happens:

A*  <- "Selected"
B
C

Now sorting descending, C on top and selected. I'd like to have still A selected:

C*  <- "Selected"
B
A   <- "Want have"

Same happens similar with multiple rows being selected. Is there a workaround for this?

like image 262
Horst Walter Avatar asked Jan 27 '11 17:01

Horst Walter


3 Answers

Here is my approach in VB.NET

Private cSelectedRow As String

Private Sub DataGridView1_CellMouseDown(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.CellMouseDown
    If e.RowIndex = -1 AndAlso DataGridView1.SelectedRows.Count > 0 Then
        cSelectedRow = DataGridView1.SelectedRows(0).Cells("ID").Value.ToString()
    End If
End Sub

I used the same events as David Hall, but did not use the BindingSource. So I loop through all Lines of the grid to find the one that was selected before.

Private Sub DataGridView1_Sorted() Handles DataGridView1.Sorted
    DataGridView1.ClearSelection()
    For Each xRow As DataGridViewRow In DataGridView1.Rows
        If xRow.Cells("ID").Value = cSelectedRow Then
            DataGridView1.CurrentCell = xRow.Cells(0)
            'Line Found. No need to loop through the rest.
            Exit For
        End If
    Next
End Sub
like image 54
Marvin Dickhaus Avatar answered Nov 09 '22 18:11

Marvin Dickhaus


You can work around this behaviour by storing away the value of the currently selected row (or rows) before sorting and then reselecting the row afterwards.

You need to use the CellMouseDown event - it is necessary to use this event since it is the only one which fires before the sort happens. Alternative events like ColumnHeaderMouseClick are all too late.

In the CellMouseDown eventhandler check that the row index is -1 to ensure that the headers were selected.

void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.RowIndex == -1)
    {
        selected = dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
    }
}

I have a class level field selected that I use to store the unique identifier of the column that is selected. If you don't have a unique id then you could add in a column for this purpose and hide it.

Then in the Sorted eventhandler of the DataGridView you can use the .Find() method of the grid's binding source:

void dataGridView1_Sorted(object sender, EventArgs e)
{
    if (!string.IsNullOrEmpty(selected))
    {
        int itemFound = _bindingSource.Find("name", selected);
        _bindingSource.Position = itemFound;
    }
}

While investigating this I found the following post on the MSDN forums where the answer uses the DataBindingComplete event - I'm not 100% why they found that necessary as my approach has worked for all my tests, but you might find it a helpful reference.

like image 25
David Hall Avatar answered Nov 09 '22 16:11

David Hall


I tried David Hall's answer but did not worked to me, on one of the three states of sorting operation in my grid, so I've changed something in my MyDataGridView.cs class, as this:

public string SelectedValue { get; set; }
private bool headerFirstClick = true;
public bool HeaderWasClicked = false;

private void MyDataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
    try
       {
            if (headerFirstClick) Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = SortOrder.None;
            if (Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.None)
            {
                Columns[e.ColumnIndex].SortMode = DataGridViewColumnSortMode.Automatic;
                Sort(Columns[e.ColumnIndex], System.ComponentModel.ListSortDirection.Ascending);
                Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
            }
                else if (Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Ascending)
            {
                Columns[e.ColumnIndex].SortMode = DataGridViewColumnSortMode.Programmatic;
                ((BindingSource)DataSource).Sort = string.Empty;
                Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = SortOrder.None;
            }
            headerFirstClick = false;
            int findValue = ((BindingSource)DataSource).Find(Columns[e.ColumnIndex].Name, SelectedValue);
            ((BindingSource)DataSource).Position = findValue;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

protected override void OnCellClick(DataGridViewCellEventArgs e)
    {
        base.OnCellClick(e);
        if (e != null)
        {
            try
            {
                HeaderWasClicked = (e.RowIndex == -1);
                SelectedValue = SelectedRows[0].Cells[e.ColumnIndex].Value.ToString();
            }
            catch(Exception){}
        }
    }
like image 1
Vali Maties Avatar answered Nov 09 '22 16:11

Vali Maties