绑定的 DataGridView 未更新以显示信息 + 排序问题

Bound DataGridView not updating to display information + sorting issues

我有一个绑定的 DataGridView1 和几个绑定的 TextBoxes 和 DateTimePickers。

一切都很好,除了当我试图同时完成这两件事时:

  1. 按特定列(日期)对 DataGridView1 进行排序。
  2. 根据 DateTimePicker 更改同一列日期。


我的想法是我像这样对 DataGridView1 进行排序(我在表单加载中设置了它):

DataGridView1.Sort(DataGridView1.Columns(45), ListSortDirection.Ascending)

(第 45 列只是我的 "MyDateColumn" 列)

后来,当我像这样更改日期列时:

DirectCast(MyBindingSource.Current, DataRowView).Item("MyDateColumn") = MyDateTimePicker.Value.AddDays(100)

DataGridView1 应自动并立即按日期排序。


好的,这就是问题所在。排序有效 - 但由于某种原因,我的 DataGridView 拒绝反映 DirectCast 设置的日期,直到我 select 另一行 。当我 select 另一行时 - then 它会更改以反映我刚刚发送的日期。
我已经尝试了所有我能想到的 - 我终于找到了解决方案:

Me.BindingContext(MyBindingSource).EndCurrentEdit()


这很好用——但前提是我不使用:

DataGridView1.Sort(DataGridView1.Columns(45), ListSortDirection.Ascending)

如果我正在对我的 DataGridView 进行排序,我的绑定似乎只是...在我 .EndCurrentEdit 之后停止工作。如果我注释掉 ListSortDirection.Ascending 行,那么效果很好!

我尝试使用 DataGridView1.Refresh() 代替 .EndCurrentEdit(),但它非常慢 - 我宁愿完全避免使用它。

有人有什么建议吗?

DGV 在单元格验证后显示编辑后的值,这似乎是 DGV 的一个问题。 为了做到这一点(被视为解决方法),您可以在单元格上设置值后 simulate/force 自己进行网格验证(调用下面的方法):

Private Sub SimulateDGVValidation()

    Try

        Dim currentCell As DataGridViewCell = Me.DataGridView1.CurrentCell

        If (currentCell) IsNot Nothing Then
            Dim c As Integer = IIf(currentCell.ColumnIndex > 1, currentCell.ColumnIndex - 1, 1)
            Me.DataGridView1.CurrentCell = Me.DataGridView1(c, currentCell.RowIndex)
        Else
            Me.DataGridView1.CurrentCell = Me.DataGridView1(0, 0)
        End If


        Application.DoEvents()

        'Return in the initial cell 
        If (currentCell) IsNot Nothing Then
            Me.DataGridView1.CurrentCell = currentCell
        End If


    Catch ex As Exception
        Console.WriteLine(ex.ToString)
    End Try

End Sub

既然BindingSource数据源是可以排序的,就用BindingSource.Sort 属性代替排序控件(你的DataGridView)。 columnName是用于排序的Column名称(可以指定多个Column):

myBindingSource.Sort = $"{columnName} ASC"

然后,当用户设置DateTimePicker.Value时,使用ValueChanged事件更新单元格值,对应于当前行的DateTime列:

方法一:绑定DateTimePicker

DateTimePicker.ValueChanged中调用了BindingSource.EndEdit()方法,立即将新值应用到数据源。

注意 1:要使此方法按预期工作,BindingSource 数据源必须是 DataTable 或实现 IEditableObject 的其他容器接口.

DateTimePicker1.DataBindings.Add(
    New Binding("Value", myBindingSource, "MyDateTimeColumn", False, DataSourceUpdateMode.OnValidation))

'(...)

Private Sub DateTimePicker1_ValueChanged(sender As Object, e As EventArgs) Handles DateTimePicker1.ValueChanged
    myBindingSource.EndEdit()
End Sub

注 2:DateTimePicker 不支持 null/DbNull 值。如果 DataSouce 可能包含 DBNull 个值,它可能会变得 不稳定 。您可能需要从中创建自定义控件,以管理其行为。否则,参见方法 2

方法二:解绑DateTimePicker

在代码中设置 DateTimePicker 值时使用 userSetValue 字段,以防止 ValueChanged 事件中的过程更新 DateTime 列的值。这样,事件只会在用户手动更改日期时更新列。

Private columnName As String = String.Empty
Private userSetValue As Boolean = True

Private Sub DateTimePicker1_ValueChanged(sender As Object, e As EventArgs) Handles DateTimePicker1.ValueChanged
    If (Not userSetValue) Then Return
    If (DataGridView1.CurrentRow Is Nothing) OrElse
        (DataGridView1.CurrentRow.Cells($"{columnName}").ValueType IsNot GetType(Date)) Then Return

    'DataGridView1.SuspendLayout()
    DataGridView1.CurrentRow.Cells($"{columnName}").Value = DateTimePicker1.Value
    myBindingSource.EndEdit()
    'DataGridView1.ResumeLayout(False)
End Sub

► 测试使用和不使用 SuspendLayout() / ResumeLayout() 的两种方法。

RowPostPaint 事件(例如)可用于更新 DateTimePicker.Value 相关列的值:

Private Sub DataGridView1_RowPostPaint(sender As Object, e As DataGridViewRowPostPaintEventArgs) Handles DataGridView1.RowPostPaint
    userSetValue = False
    Dim currentValue = DataGridView1.CurrentRow.Cells("MyDateTimeColumn").Value
    If currentValue Is Nothing OrElse currentValue Is DBNull.Value Then currentValue = Date.Now
    DateTimePicker1.Value = DirectCast(currentValue, Date)
    userSetValue = True
End Sub

使用这两种方法中的任何一种,结果如下: