DGV RowPrepaint Indexfehler - Bug?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    DGV RowPrepaint Indexfehler - Bug?

    Hallo zusammen.

    Ich lasse meine DGV-Rows bei bestimmten Bedingungen färben. Im Normalfall klappt das, ab und an kommen allerdings (mir unerklärliche) Exceptions bzgl. RowIndex.
    Hier mal der Code für's RowPrepaint:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub dgv_RowPrePaint(sender As Object, e As DataGridViewRowPrePaintEventArgs) Handles dgvMitarbeiterVertrag.RowPrePaint,
    2. dgvVertrag.RowPrePaint
    3. 'TODO: Indexfehler - wie umgehen?
    4. 'System.ArgumentOutOfRangeException: "Der Index lag außerhalb des Bereichs. Er darf nicht negativ und kleiner als die Sammlung sein.
    5. 'Parametername: index"
    6. 'erstmal deaktiviert, bis eine Lösung gefunden wurde.
    7. Select Case True
    8. Case sender Is dgvMitarbeiterVertrag
    9. Dim rw = DirectCast(DirectCast(dgvMitarbeiterVertrag.Rows(e.RowIndex).DataBoundItem, DataRowView).Row, MitarbeiterVertragRow)
    10. dgvMitarbeiterVertrag.ConditionedDgvRowForeColor(e, Not rw.Aktiv)
    11. Case sender Is dgvVertrag
    12. Dim rw = DirectCast(DirectCast(dgvVertrag.Rows(e.RowIndex).DataBoundItem, DataRowView).Row, VertragRow)
    13. dgvMitarbeiterVertrag.ConditionedDgvRowForeColor(e, Not rw.Aktiv)
    14. End Select
    15. End Sub


    Und hier die Fehlermeldung:


    Wie man sieht, befinden sich 4 Rows im DGV, heißt der Index darf maximal "3" betragen. Er kommt aber mit "4" in der Methode an - kann ich mir nicht erklären, weil der
    RowIndex im Eventhandler über die DataGridViewRowPrePaintEventArgs zur Methode weitergegeben wird. Wie kann denn da ne 4 rauskommen, obwohl der Max-Wert 3 betragen darf?

    Wie kann ich da elegant eine Prüfung drauf legen, dass mir das Programm nicht abschmiert?

    EDIT: Hab die Methode mal wie folgt umgebaut - aber meiner Meinung nach dürfte es eine solche Prüfung gar nicht nötig sein:

    VB.NET-Quellcode

    1. <Extension>
    2. Public Sub ConditionedDgvRowForeColor(dgv As DataGridView, RowIndex As Integer, condition As Boolean)
    3. If Not RowIndex < 0 AndAlso Not RowIndex > dgv.Rows.Count - 1 Then dgv.Rows(RowIndex).DefaultCellStyle.ForeColor = If(condition, Color.Red, SystemColors.ControlText)
    4. End Sub
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „tragl“ ()

    Wenn das DGV so eingestellt ist, dass der User selber Zeilen hinzufügen kann, ist ja die letzte Zeile immer leer und es wird eine weitere hinzugefügt, sobald man in die letzte was reinschreibt. Wird vermutlich daran liegen, aber ohne nen Screenshot oder die DGV-Einstellungen zu sehen nicht zu beurteilen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    dass der User selber Zeilen hinzufügen kann

    ist eben nicht so, ist bei allen DGV's (ich glaub mit einer Ausnahme) im ganzen Projekt deaktiviert.... also hier in dem Fall kann und darf es keinen RowIndex 4 geben wenn nur 4 Zeilen da sind ;)

    Edit:
    Also die DGV's werden beim Laden der jeweiligen Form schon voreingstellt (Projektweit) - egal was im Designer eingestellt ist. Wöllte ich jetzt, dass ein User da im DGV selbst was hinzufügen soll müsste ich das im jeweiligen
    frm_Load-Event angeben: dgv.AllowUserToAddRows = True

    Spoiler anzeigen
    Zeile 57

    VB.NET-Quellcode

    1. ''' <summary> Überschreibt das eingestellte "Styling" des DataGridView </summary>
    2. Public Shared Sub StyleDGV(frm As ContainerControl)
    3. Dim getChilds As Func(Of Control, IEnumerable) = Function(ctl) ctl.Controls '.Controls
    4. For Each ctl In getChilds.All(frm)
    5. Dim grd = TryCast(ctl, DataGridView)
    6. If grd.NotNull Then
    7. Dim tbl = DirectCast(grd.DataSource, BindingSource)?.DataTable
    8. If tbl.NotNull Then
    9. For Each dgvCol As DataGridViewColumn In grd.Columns
    10. Dim propName = dgvCol.DataPropertyName
    11. If propName = "" Then Continue For
    12. Dim dataCol = tbl.Columns(propName)
    13. If dataCol.ReadOnly = True Then dgvCol.DefaultCellStyle.BackColor = SystemColors.Menu
    14. If dataCol.DataType = GetType(Decimal) Then
    15. With dgvCol.DefaultCellStyle
    16. .Alignment = DataGridViewContentAlignment.MiddleLeft
    17. '.Format = "c"
    18. '.Font = New Font("Courier New", If(.Font, If(grd.DefaultCellStyle.Font, grd.Font)).Size)
    19. End With
    20. ElseIf dataCol.DataType = GetType(Double) Then
    21. With dgvCol.DefaultCellStyle
    22. .Alignment = DataGridViewContentAlignment.MiddleLeft
    23. ' .Format = "n2"
    24. ' .Font = New Font("Courier New", If(.Font, If(grd.DefaultCellStyle.Font, grd.Font)).Size)
    25. End With
    26. ElseIf dataCol.DataType = GetType(DateTime) Then
    27. 'With dgvCol.DefaultCellStyle
    28. ' .Format = "dd.MM.yyyy"
    29. 'End With
    30. ElseIf dataCol.DataType = GetType(TimeSpan) Then
    31. With dgvCol.DefaultCellStyle
    32. .Format = "hh\:mm"
    33. End With
    34. ElseIf dataCol.DataType = GetType(Int32) Then
    35. With dgvCol.DefaultCellStyle
    36. .Alignment = DataGridViewContentAlignment.MiddleLeft
    37. End With
    38. ElseIf dataCol.DataType = GetType(System.String) Then
    39. With dgvCol.DefaultCellStyle
    40. .Alignment = DataGridViewContentAlignment.MiddleLeft
    41. End With
    42. End If
    43. Next
    44. End If
    45. With grd
    46. .AllowUserToResizeRows = False
    47. .AllowUserToResizeColumns = True
    48. .RowHeadersWidth = 25
    49. .Attach.UnFocusedSelectionColor()
    50. '.CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal
    51. .CellBorderStyle = DataGridViewCellBorderStyle.Raised
    52. .SelectionMode = DataGridViewSelectionMode.FullRowSelect
    53. .ShowEditingIcon = False
    54. .RowHeadersVisible = False
    55. .BackgroundColor = SystemColors.Window
    56. 'Designer-Aufgaben-Einstellung
    57. .AllowUserToAddRows = False
    58. .ReadOnly = True
    59. .AllowUserToDeleteRows = False
    60. .AllowUserToOrderColumns = False
    61. End With
    62. AddHandler grd.DataError, AddressOf DgvErrorHandling
    63. End If
    64. Next
    65. End Sub


    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „tragl“ ()

    Ok, woher kommt der RowIndex, den Du in die Methode reinschiebst?
    @Gerrit: Laut Screenshot in Post#1: 4 Rows mit den Indizes 0-3, RowCount = 4

    ##########

    Ok, der RowIndex kommt vom PrePaint-Event. Dementsprechend müsstest Du schon beim ersten EventHandler schauen, wie da die DGV-Werte sind.
    Will heißen, gleich in Private Sub dgv_RowPrePaint als erste Zeile schreiben:

    VB.NET-Quellcode

    1. If (DirectCast(sender, DataGridView).RowCount <= e.RowIndex Then Throw New ArgumentException
    Dann wird rechtzeitig klar, dass Du da gar nicht weiterarbeiten musst und Du kannst, sobald die Exception auftritt und das Programm stoppt, ggf. über die Aufrufliste oder das Anschauen der sender-/DGV-Details herausfinden, wo der Wolf verrbuddelt liegt.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „VaporiZed“ ()

    Neu

    VaporiZed schrieb:

    Will heißen, gleich in Private Sub dgv_RowPrePaint als erste Zeile schreiben:


    jo hab ich gemacht, kommt keine Exception - aber trotzdem der gleiche Fehler...
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Neu

    Moment. Dann stimmt aber was grundsätzlich nicht. Denn dann müsste ja immer RowCount > e.RowIndex sein und damit dürfte es später die RowIndexException nicht geben. Oder ändert sich zwischendurch dier RowCount? Kann ja eigentlich auch nicht sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Neu

    @VaporiZed jo, ist seltsam alles. Hab mir mal ein paar Debug-Ausgaben gesetzt:

    Quellcode

    1. 5 RowPrePaint DGV: dgvVertrag
    2. 6 RowPrePaint dgv.RowCount: 5
    3. 7 RowPrePaint e.RowIndex 3
    4. 8 Painting1 e.RowIndex: 3
    5. 9 Painting2-RowCount: 4
    6. 0 Painting2-RowIndex: 3
    7. 1 Painting2 erfolgreich
    8. 2 RowPrePaint DGV: dgvVertrag
    9. 3 RowPrePaint dgv.RowCount: 5
    10. 4 RowPrePaint e.RowIndex 4
    11. 5 Painting1 e.RowIndex: 4
    12. 6 Painting2-RowCount: 4
    13. 7 Painting2-RowIndex: 4
    14. Ausnahme ausgelöst: "System.ArgumentOutOfRangeException" in mscorlib.dll
    15. Eine Ausnahme vom Typ "System.ArgumentOutOfRangeException" ist in mscorlib.dll aufgetreten, doch wurde diese im Benutzercode nicht verarbeitet.
    16. Der Index lag außerhalb des Bereichs. Er darf nicht negativ und kleiner als die Sammlung sein.


    Ab Zeile 9 sieht mann, dass sich der RowCount ändert von 5 auf 4 und somit passen die Indexes nicht mehr zusammen ?(
    Hier noch die Methoden in Aufrufreihenfolge:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub dgv_RowPrePaint(sender As Object, e As DataGridViewRowPrePaintEventArgs) Handles dgvMitarbeiterVertrag.RowPrePaint,
    2. dgvVertrag.RowPrePaint
    3. Dim dgv = DirectCast(sender, DataGridView)
    4. Dbg($"RowPrePaint DGV: {dgv.Name}")
    5. Dbg($"RowPrePaint dgv.RowCount: {dgv.Rows.Count}")
    6. Dbg($"RowPrePaint e.RowIndex {e.RowIndex}")
    7. Select Case True
    8. Case sender Is dgvMitarbeiterVertrag
    9. Dim rw = DirectCast(DirectCast(dgvMitarbeiterVertrag.Rows(e.RowIndex).DataBoundItem, DataRowView).Row, MitarbeiterVertragRow)
    10. dgvMitarbeiterVertrag.ConditionedDgvRowForeColor(e, Not rw.Aktiv)
    11. Case sender Is dgvVertrag
    12. Dim rw = DirectCast(DirectCast(dgvVertrag.Rows(e.RowIndex).DataBoundItem, DataRowView).Row, VertragRow)
    13. dgvMitarbeiterVertrag.ConditionedDgvRowForeColor(e, Not rw.Aktiv)
    14. End Select
    15. End Sub


    Spoiler anzeigen

    VB.NET-Quellcode

    1. <Extension>
    2. Public Sub ConditionedDgvRowForeColor(dgv As DataGridView, e As DataGridViewRowPrePaintEventArgs, condition As Boolean)
    3. Dbg($"Painting1 e.RowIndex: {e.RowIndex}")
    4. ConditionedDgvRowForeColor(dgv, e.RowIndex, condition)
    5. End Sub


    Spoiler anzeigen

    VB.NET-Quellcode

    1. <Extension>
    2. Public Sub ConditionedDgvRowForeColor(dgv As DataGridView, RowIndex As Integer, condition As Boolean)
    3. 'If Not RowIndex < 0 AndAlso Not RowIndex > dgv.Rows.Count - 1 Then dgv.Rows(RowIndex).DefaultCellStyle.ForeColor = If(condition, Color.Red, SystemColors.ControlText)
    4. Dbg($"Painting2-RowCount: {dgv.Rows.Count}")
    5. Dbg($"Painting2-RowIndex: {RowIndex}")
    6. dgv.Rows(RowIndex).DefaultCellStyle.ForeColor = If(condition, Color.Red, SystemColors.ControlText)
    7. Dbg("Painting2 erfolgreich")
    8. End Sub


    Edit: Manchmal sieht man auch einfach vor lauter Bäumen den Wald nicht mehr. Fehler gefunden. Im 1. Spoiler in Zeile 13 gehört natürlich
    statt dgvMitarbeiterVertrag das korrekte dgv dgvVertrag rein :rolleyes:
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup: