Выбранные строки при сортировке DataGridView в приложении WinForm

В приложении WinForm, С# 4.0, у меня есть DataGridView, связанный с SortableBindingList. Следовательно, его можно отсортировать, щелкнув по столбцу заголовка - все до сих пор прекрасно, -)

Проблема заключается в том, что выбранные строки, по-видимому, "запоминаются" номером строки. Вот что происходит:

A*  <- "Selected"
B
C

Теперь сортировка по убыванию, C сверху и выбрана. Я бы хотел, чтобы еще выбран A:

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

То же самое происходит при выборе нескольких строк. Есть ли обходной путь для этого?

Ответ 1

Вы можете обойти это поведение, сохранив значение текущей выбранной строки (или строк) перед сортировкой, а затем повторно выбрав строку впоследствии.

Вам нужно использовать событие CellMouseDown - необходимо использовать это событие, поскольку оно является единственным, которое срабатывает до того, как произойдет сортировка. Альтернативные события, такие как ColumnHeaderMouseClick, слишком поздно.

В обработчике событий CellMouseDown проверьте, что индекс строки равен -1, чтобы гарантировать, что заголовки были выбраны.

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

У меня есть поле уровня класса selected, которое я использую для хранения уникального идентификатора выбранного столбца. Если у вас нет уникального идентификатора, вы можете добавить в колонку для этой цели и скрыть его.

Затем в обработчике событий Sorted DataGridView вы можете использовать метод .Find() источника привязки сетки:

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

При этом я обнаружил следующее сообщение на форумах MSDN, где ответ использует событие DataBindingComplete - я не 100%, почему они что это необходимо, поскольку мой подход работал на все мои тесты, но вы можете найти его полезной ссылкой.

Ответ 2

Вот мой подход в 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

Я использовал те же события, что и David Hall, но не использовал BindingSource. Поэтому я просматриваю все строки сетки, чтобы найти ту, которая была выбрана раньше.

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