在WinForm应用程序中对DataGridView进行排序时选择行。

11
在一个C# 4.0的WinForm应用程序中,我有一个绑定到SortableBindingList的DataGridView。因此,可以通过单击标题列来对其进行排序 - 到目前为止都很好;问题在于,所选行似乎是按行号“记住”的。以下是发生的情况:
A*  <- "Selected"
B
C

现在是按照降序排序,C在最上面且被选中。我希望A仍然被选中:

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

同样的问题也会出现在选择多行时。有什么解决方法吗?


你是正确的。所选行是通过行索引而不是行值“记住”的。 - Tony Abrams
ASP .NET 4为GridView引入了EnablePersistedSelection属性。但是它似乎不适用于WinForms。http://www.asp.net/learn/whitepapers/aspnet4#0.2__Toc253429262 - Ranhiru Jude Cooray
3个回答

12
你可以通过在排序之前存储当前选定行(或行)的值,然后在排序后重新选择该行来解决此问题。
你需要使用CellMouseDown事件 - 必须使用此事件,因为它是在排序发生之前唯一触发的事件。类似ColumnHeaderMouseClick的替代事件都太晚了。
在CellMouseDown事件处理程序中检查行索引是否为-1,以确保已选择标题。
void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.RowIndex == -1)
    {
        selected = dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
    }
}

我有一个类级别的字段selected,用于存储所选列的唯一标识符。如果您没有唯一的ID,则可以添加一个用于此目的的列并将其隐藏。

然后,在 DataGridView 的 Sorted 事件处理程序中,您可以使用网格绑定源的 .Find() 方法:

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

在调查过程中,我发现了在MSDN论坛上的这篇帖子,其中回答使用了DataBindingComplete事件--虽然我不确定为什么他们认为那是必要的,因为我的方法已经适用于我所有的测试,但你可能会发现它是一个有用的参考。


5

这是我的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

1

我尝试了David Hall的答案,但在我的网格中排序操作的三种状态之一上并没有起作用,因此我更改了MyDataGridView.cs类中的某些内容,如下所示:

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){}
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接