Bound DataGridView not updating to display information + sorting issues

狂风中的少年 提交于 2020-02-05 13:16:12

问题


I have a bound DataGridView1 and several bound TextBoxes and DateTimePickers.

Everything is working well, except when I try to accomplish these two things in conjunction:

  1. Sort the DataGridView1 by a specific column (date).
  2. Change that same column date based on a DateTimePicker.


The idea is that I am sorting my DataGridView1 like so (I set this in Form Load):

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

(Column 45 is just my "MyDateColumn" column)

And later on, when I change my date column like so:

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

The DataGridView1 should automatically and immediately sort by the date.


Okay, so here's where the issue is. The sorting works - but for some reason my DataGridView refuses to reflect the date set by the DirectCast until I select another row. When I select another row - then it changes to reflect the date I just sent.
I've tried everything I could think of - and I finally found a solution:

Me.BindingContext(MyBindingSource).EndCurrentEdit()


This works great - but only if I am not using:

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

If I'm sorting my DataGridView, my binding seems to just...stop working after I .EndCurrentEdit. If I comment out that ListSortDirection.Ascending line, then it works great!

I tried to use DataGridView1.Refresh() in lieu of .EndCurrentEdit(), but it is exceptionally slow - I would rather avoid using that altogether.

Does anyone have any advice?


回答1:


Since the BindingSource data source can be sorted, use the BindingSource.Sort property instead of sorting the control (your DataGridView). columnName is the name of the Column used for sorting (you can specify more than one Column):

myBindingSource.Sort = $"{columnName} ASC"

Then, when the User sets the DateTimePicker.Value, update the Cell value, corresponding to DateTime Column of the current Row, using the ValueChanged event:

Method 1: Bound DateTimePicker

In the DateTimePicker.ValueChanged, the BindingSource.EndEdit() method is called, to apply immediately the new value to the data source.

Note 1: for this method to work as expected, the BindingSource data source must be a DataTable or another container that implements the IEditableObject interface.

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

Note 2: the DateTimePicker doesn't support a null/DbNull value. If the DataSouce may contain DBNull values, it may become erratic. You'll probably need to create a custom control from it, to manage its behavior. Otherwise, see Method 2

Method 2: UnBound DateTimePicker

The userSetValue field is used when setting the DateTimePicker value in code, to prevent the procedure in the ValueChanged event to update the DateTime Columns's Value. This way, the event will only update the Column when a User changes the date manually.

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

► Test both methods with and without SuspendLayout() / ResumeLayout().

The RowPostPaint event (for example) can be used to update the DateTimePicker.Value the associated Columns' 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

Using any of the two methods, this is the result:




回答2:


DGV shows edited value after cell validation and this seems to be an issue around DGV. In order to do that (considered as workaround) you can simulate/force by yourself your grid validation (calling the method below) after you have setted the value on your cell:

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


来源:https://stackoverflow.com/questions/58862289/bound-datagridview-not-updating-to-display-information-sorting-issues

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!