Datagridview mit Datatable wirft System.IndexOutOfRangeException

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

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von Rizzle.

    Datagridview mit Datatable wirft System.IndexOutOfRangeException

    Moin,

    ich habe ein Datagridview das auflaufende Fehler anzeigen soll und dazu habe ich sie mit einer Datatable verbunden in der ich die Datanaktualisiere bzw. hinzufüge und lösche.
    In einem Thread wird im hintergrund die ganze Zeit gelesen und der aktualisiert die Datatable.

    Ich habe mir jetzt ein Textprogramm geschrieben, um mein Programm kaputt zu bekommen und hab es leider geschafft. Ab und zu wird die Meldung "System.IndexOutOfRangeException" generiert.

    So im ganzen:

    2022-08-25 17:18:11,503 [1] ERROR QSorting - System.IndexOutOfRangeException: Der Index 15 hat keinen Wert.
    bei System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
    bei System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 rowIndex) -- System.Windows.Forms.DataGridView.OnDataErrorInternal > System.Windows.Forms.DataGridView.OnDataError > QSorting.Alarmbox.Alarmbox_DataError

    Da ich zuerst der Meinung war, dass es am Hintergrundthread liegt, habe ich mehere Syncmethoden eingebaut, die alle keinen Erfolg hatten. Es kann meines Erachtens auch nicht schuld sein, weil nur der Thread schreiben schreibt und löscht, kein 2. oder 3.

    So habe ich meine Datagridview erstellt:

    VB.NET-Quellcode

    1. Public Class Alarmbox
    2. Inherits DataGridView
    3. Sub New() 'ByRef Alarmtable As Alarmboxdatatable)
    4. Try
    5. 'Nicht zur Designzeit ausfuehren:
    6. If System.ComponentModel.LicenseManager.UsageMode = System.ComponentModel.LicenseUsageMode.Designtime Then Return
    7. ' Dieser Aufruf ist für den Designer erforderlich.
    8. InitializeComponent()
    9. 'Werte aus der Einstellung uebernehmen
    10. Me.Columns.Clear()
    11. DataGridSettings()
    12. Me.DoubleBuffered = True
    13. RemoveHandler MyBase.Resize, AddressOf Alarmbox_Resize
    14. AddHandler MyBase.Resize, AddressOf Alarmbox_Resize
    15. Catch ex As Exception
    16. Log.Error(ex)
    17. End Try
    18. End Sub
    19. Private Sub DataGridSettings()
    20. Try
    21. 'Einstellung vom Datagrid:
    22. Me.BackgroundColor = Color.Gray
    23. 'Set the selection background color for all the cells.
    24. Me.DefaultCellStyle.SelectionBackColor = Color.Red
    25. Me.DefaultCellStyle.SelectionForeColor = Color.White
    26. Me.RowsDefaultCellStyle.BackColor = Color.Red
    27. Me.RowsDefaultCellStyle.ForeColor = Color.White
    28. 'Alternating:
    29. 'Me.AlternatingRowsDefaultCellStyle.BackColor = Color.DarkGray
    30. Me.AllowUserToAddRows = False
    31. Me.AllowUserToDeleteRows = False
    32. Me.AllowUserToOrderColumns = True
    33. Me.ReadOnly = True
    34. Me.SelectionMode = DataGridViewSelectionMode.FullRowSelect
    35. Me.MultiSelect = False
    36. Me.RowHeadersVisible = False
    37. Me.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
    38. Me.AllowUserToResizeColumns = False
    39. Me.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
    40. Me.AllowUserToResizeRows = False
    41. Me.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing
    42. Catch ex As Exception
    43. Log.Error(ex)
    44. End Try
    45. End Sub
    46. Private Sub Alarmbox_Resize(sender As Object, e As EventArgs) 'Handles MyBase.Resize
    47. Try
    48. ResizeColumns()
    49. Catch ex As Exception
    50. Log.Error(ex)
    51. End Try
    52. End Sub
    53. Public Sub ResizeColumns()
    54. Try
    55. If Me IsNot Nothing Then
    56. If Me.InvokeRequired Then
    57. Return
    58. End If
    59. If Me.Columns.Count < 3 Then Return
    60. Dim Gridwidth = Me.Width
    61. Me.Columns(0).Width = CInt(Gridwidth * 0.2) - 7
    62. Me.Columns(1).Width = CInt(Gridwidth * 0.2) - 7
    63. Me.Columns(2).Width = CInt(Gridwidth * 0.6) - 7
    64. Me.VerticalScrollBar.Visible = False
    65. Me.Refresh()
    66. End If
    67. Catch ex As Exception
    68. Log.Error(ex)
    69. End Try
    70. End Sub
    71. Private Sub Alarmbox_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles MyBase.DataBindingComplete
    72. Try
    73. Me.Columns(3).Visible = False
    74. RemoveHandler MyBase.DataBindingComplete, AddressOf Alarmbox_DataBindingComplete
    75. Catch ex As Exception
    76. Log.Error(ex)
    77. Log.Info("Fehler bei Alarmbox_DataBindingComplete")
    78. End Try
    79. End Sub
    80. Private Sub Alarmbox_DataError(ByVal sender As Object, ByVal e As DataGridViewDataErrorEventArgs) Handles Me.DataError
    81. Log.Error(e.Exception)
    82. ShowError(e.Exception)
    83. End Sub
    84. End Class




    Und so habe ich meine Datatable erstellt:


    VB.NET-Quellcode

    1. Public Class Alarmboxdatatable
    2. Inherits DataTable
    3. Private semi As New Semaphore(1, 1)
    4. Private _Alarmbox As Alarmbox
    5. Public Structure _Alarmrow
    6. Dim Datetime As DateTime
    7. Dim Group As String
    8. Dim Alarmtext As String
    9. End Structure
    10. Private _Alarmdic As New Dictionary(Of Integer, _Alarmrow)
    11. Sub New(ByRef Alarmbox As Alarmbox)
    12. Dim Lang As String = My.Settings.Language
    13. If Lang = "en" Then
    14. Me.Columns.Add("Time", GetType(DateTime))
    15. Me.Columns.Add("Group", GetType(String))
    16. Me.Columns.Add("Alarmtext", GetType(String))
    17. Me.Columns.Add("ID", GetType(Integer))
    18. Me.PrimaryKey = New DataColumn() {Me.Columns("ID")}
    19. Else
    20. Me.Columns.Add("Zeit", GetType(DateTime))
    21. Me.Columns.Add("Gruppe", GetType(String))
    22. Me.Columns.Add("Alarmtext", GetType(String))
    23. Me.Columns.Add("ID", GetType(Integer))
    24. Me.PrimaryKey = New DataColumn() {Me.Columns("ID")}
    25. End If
    26. _Alarmbox = Alarmbox
    27. End Sub
    28. 'Public Property Alarmdic As Dictionary(Of Integer, _Alarmrow)
    29. 'Set(value As Dictionary(Of Integer, _Alarmrow))
    30. '_Alarmdic = value
    31. 'End Set
    32. 'Get
    33. 'Return _Alarmdic
    34. 'End Get
    35. 'End Property
    36. <MethodImpl(MethodImplOptions.Synchronized)>
    37. Friend Sub updateAlarmtabe(ID As Integer, Time As DateTime, Group As String, Alarmtext As String, Value As Boolean) ' As Alarmboxdatatable
    38. If _Alarmbox Is Nothing Then Exit Sub
    39. Dim DeliResume As New AlarmResumeDelegate(AddressOf AlarmResume)
    40. Dim DeliSuspent As New AlarmSuspendDelegate(AddressOf AlarmSuspendLayout)
    41. semi.WaitOne()
    42. _Alarmbox.Invoke(DeliSuspent)
    43. Try
    44. If Value Then
    45. If _Alarmdic.ContainsKey(ID) Then
    46. 'nichts
    47. Else
    48. Try
    49. Dim newRow = Me.NewRow
    50. newRow.Item(0) = Time
    51. newRow.Item(1) = Group
    52. newRow.Item(2) = Alarmtext
    53. newRow.Item(3) = ID
    54. Me.Rows.InsertAt(newRow, 0)
    55. _Alarmdic.Add(ID, New _Alarmrow With {.Datetime = Time, .Group = Group, .Alarmtext = Alarmtext})
    56. Catch ex As Exception
    57. Log.Error(ex)
    58. ' ShowError(ex)
    59. End Try
    60. End If
    61. Else
    62. If Not _Alarmdic.ContainsKey(ID) Then
    63. 'nichts
    64. Else
    65. Try
    66. If Me.Rows.Count >= 0 Then
    67. SyncLock Me
    68. _Alarmdic.Remove(ID)
    69. For i = 0 To (Me.Rows.Count - 1)
    70. If CInt(Me.Rows(i)(3)) = ID Then
    71. Try
    72. If Me.Rows.Count >= i AndAlso Me.Rows.Contains(ID) Then Me.Rows.RemoveAt(i)
    73. Catch ex As Exception
    74. Log.Error(ex)
    75. End Try
    76. Exit For
    77. End If
    78. Next
    79. End SyncLock
    80. Else
    81. MessageBox.Show("Row count kleiner gleich null")
    82. End If
    83. Catch ex As Exception
    84. Log.Error(ex)
    85. ' ShowError(ex)
    86. End Try
    87. End If
    88. End If
    89. Catch ex As Exception
    90. Log.Error(ex)
    91. ' ShowError(ex)
    92. Finally
    93. _Alarmbox.Invoke(DeliResume)
    94. semi.Release()
    95. End Try
    96. End Sub
    97. Private Delegate Sub AlarmRefreshDelegate()
    98. Private Sub AlarmRefresh()
    99. _Alarmbox.ResizeColumns()
    100. End Sub
    101. Private Delegate Sub AlarmResumeDelegate()
    102. Private Sub AlarmResume()
    103. _Alarmbox.ResumeLayout()
    104. End Sub
    105. Private Delegate Sub AlarmSuspendDelegate()
    106. Private Sub AlarmSuspendLayout()
    107. _Alarmbox.SuspendLayout()
    108. End Sub
    109. End Class



    In der Datatable benutze ich die Funktion Update, um die Daten hinzuzufügen oder zu entfernen.
    Da der Fehler sporadisch ist, weiß ich nicht mal wie ich den provoziere und wieso er versucht wird.

    Google bringt mich momentan nicht weiter und ansonst läuft das Programm komplett stabil.

    Kann mir einer das Phaenomen erklären oder mir sagen was ich übersehe?
    Ich frage ja extra ab, ob es die Zeile in der Tabelle gibt und trotzdem kommt der Fehler ?(

    Viele Grüße
    @Rizzle Schmeiß zunächst alle Try-Catch-Blöcke raus, Du willst doch die Fehler finden und nicht verstecken.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Die habe ich alle reingemacht, weil ich gehofft habe das er was in meine Logdatei schreibt. Bin da außer bei Dateizugriffe sonst kein Fan von.
    Leider kein Erfolg, außer das:

    Das DGV löst ja das Ereignis aus:

    Private Sub Alarmbox_DataError(ByVal sender As Object, ByVal e As DataGridViewDataErrorEventArgs) Handles Me.DataError
    Log.Error(e.Exception)
    ShowError(e.Exception)
    End Sub

    Der Text oben ist aus der Logdatei, also die Exception die sonst geworfen werden würde.

    Rizzle schrieb:

    weil ich gehofft habe das er was in meine Logdatei schreibt.
    Nein.
    Es muss an Ort und Stelle knallen, dann hast Du z.B. auch die Zeile identifiziert und alle Werte, die nicht lokal im Try-Block stehen..
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wenn der Compiler sagt, dass es kein Item mit dem Index X gibt, dann ist es so. Das bringt Dir aber nix, solange Du nicht weißt, woher es kommt. Daher solltest Du - wenn man Dich vom Loggen nicht abhalten kann - auch loggen, wodurch da was sortiert wird und wieviele Zeilen das DGV hat und wieviele Einträge zum Fehlerzeitpunkt die DataTable. Asynchron im GUI zu arbeiten ist immer gefährlich fehleranfällig.
    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.
    Die ganze Architektur ist komisch, unüblich aufwändig.
    Am einfachsten wäre ein typDataset, und ein DGV, was daran gebunden wird.
    Ein SpezialDGV namens "Alarmbox" zu basteln, und da iwas mit einem Semi zu veranstalten - ist mir ziemlich undurchsichtig, das.

    Ein Nebenthread könnte Daten einsammeln - irgendwohin, aber nicht in die DataTable im typDataset.
    Alle paar Sekunden (WinForms-Timer-gesteuert) könnte eine Methode die Daten sichten, und inne DataTable einpflegen - das muss im HauptThread geschehen.

    eine ConcurrentQueue ist für sowas gut geeignet: Die ist dafür da, dass ein NebenThread Daten reinschmeisst, und ein HauptThread Daten entnimmt.
    Ist aber ein ziemlich schräger Vogel, so ein ConcurrentQueue.
    Aber immer noch besser als was selbstgebasteltes.
    Moin,

    Also ich lese im Hintergrund eine Steuerung aus und da wäre leider "alle" Paar Sekunden zu langsam. Ich weiß das ich alles aufwendig gestaltet habe, das ist das Problem wenn man soviele Ideen hat.
    Ich bin aber für alle Vorschläge offen, weil ich mit dem Fehler wirklich nicht weiter weiß.

    Also ich erstelle ein TyDataSet mit dem DGV das ich über den Designer so gestalte das es so aussieht wie meine jetzige Alarmanzeige.
    Soweit komme ich mit, deinen letzten Absatz verstehe ich leider nicht richtig, ConcurrentQueue ist mir neu.

    Damit könnte ich die Thread synchronisieren?
    Hallo Rizzle

    Ich hatte vor ein paar Tagen eine ähnliche Fehlermeldung. Ich bin auch daran fast verzweifelt. Ich habe damals einen neuen Datensatz per Code eingefügt. Meine Analyse war, dass das Datagridview sich dann selbständig gemacht hat und einen weiteren Dateinsatz eingefügt hat. Ich habe dann den Datensatz über das Binding eingefügt und mit Cast weiterverarbeitet.

    Anbei der auskommentierte Code, welcher Fehler machte, und der funktionierende. Ich habe mir auch noch überlegt, ob man einfach das Datagridview trennt und wieder neu bindet..

    Hoffe es hilft dir..

    VB.NET-Quellcode

    1. 'Dim Row = ZeiterfassungDataSet.KST_MA.NewKST_MARow
    2. 'Row.MANR = MA_TagRow.MANr
    3. 'Row.datum = MA_TagRow.Datum
    4. 'Row.KST = MA_TagRow.KST
    5. 'Row.Zeit_Min = MA_TagRow.Anwesenheit
    6. 'ZeiterfassungDataSet.KST_MA.AddKST_MARow(Row)
    7. 'Me.KST_MABindingSource.MoveFirst()
    8. KST_MABindingSource.AddNew()
    9. Dim row = DirectCast(DirectCast(Me.KST_MABindingSource.Current, DataRowView).Row, ZeiterfassungDataSet.KST_MARow)
    10. row.MANR = MA_TagRow.MANr
    11. row.datum = MA_TagRow.Datum
    12. row.KST = MA_TagRow.KST
    13. row.Zeit_Min = MA_TagRow.Anwesenheit


    lg Panter

    Rizzle schrieb:

    Soweit komme ich mit, deinen letzten Absatz verstehe ich leider nicht richtig, ConcurrentQueue ist mir neu.

    Damit könnte ich die Thread synchronisieren?
    Dochdoch - verstehst du richtig.

    Dass ConcurrentQueue(Of T) dir neu ist, ist kein ShowStopper - musste halt lernen.
    ConcurrentQueue kannste ganz einfach im ObjectBrowser finden - ich finde, ist eiglich ganz gut selbsterklärend - aber bei Fragen fragen.



    von wegen alle paar Sekunden: kann natürlich auch alle paar zig Millisekunden sein. Aber zu schnell würde ich sowas nicht takten: Threadwechsel sind teuer, und da hat man lieber wenige, in denen jeweils eine grössere Datenmenge in einem Rutsch verarbeitet wird.

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

    Moin Leute,

    Also ich habe jetzt ein TypDataset mit PrimaryKey gemacht und eine ConcurrentQueue (Of T), welche ich im Thread beschreibe.

    Das ist meine Queue:

    VB.NET-Quellcode

    1. Public _AlarmQueue As New Concurrent.ConcurrentQueue(Of _Alarmrow)
    2. Public Structure _Alarmrow
    3. Dim ID As Integer
    4. Dim Datetime As DateTime
    5. Dim Group As String
    6. Dim Alarmtext As String
    7. Dim State As Boolean
    8. End Structure


    Und das ist mein Timer im GUI Thread:

    VB.NET-Quellcode

    1. Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
    2. For Each Alarmrow As SendRecieve._Alarmrow In Me._SendRecieve._AlarmQueue
    3. With Alarmrow
    4. If .State Then
    5. If Me.Alarme.Alarmtable.Rows.Count > 0 Then
    6. If Not Me.Alarme.Alarmtable.Rows.Contains(.ID) Then
    7. Me.Alarme.Alarmtable.AddAlarmtableRow(.ID, .Datetime, .Group, .Alarmtext)
    8. End If
    9. Else
    10. Me.Alarme.Alarmtable.AddAlarmtableRow(.ID, .Datetime, .Group, .Alarmtext)
    11. End If
    12. Else
    13. For Each Alarm As DataRowView In Me.AlarmtableBindingSource
    14. With CType(Alarm.Row, Alarme.AlarmtableRow)
    15. If .ID = Alarmrow.ID Then
    16. Me.Alarme.Alarmtable.RemoveAlarmtableRow(CType(Alarm.Row, Alarme.AlarmtableRow))
    17. End If
    18. End With
    19. Next
    20. End If
    21. End With
    22. Next
    23. End Sub


    Ich verstehe das richtig, dass wenn ich ein Element gelesen habe, dass es aus der Queue gelöscht wird oder?
    Ich glaube nämlich sie wird immer größer und die Gui beginnt dann zu ruckeln?


    Hinzufügen tue ich die daten (vereinfach dargestellt) im Hintergrundthread so:

    VB.NET-Quellcode

    1. _AlarmQueue.Enqueue(NewAlarmrow(-1, Date.Now, "PLC", "Read Error", False))
    2. Private Function NewAlarmrow(ID As Integer, Datetime As Date, Grouptext As String, Alarmtext As String, state As Boolean) As _Alarmrow
    3. With NewAlarmrow
    4. .ID = ID
    5. .Datetime = Datetime
    6. .Group = Grouptext
    7. .Alarmtext = Alarmtext
    8. .State = state
    9. End With
    10. End Function



    Hab ich irgendwas nicht beachtet? Sonst lauf ich jetzt ins Labor und lass das über nacht laufen.




    Edit: Ich habe die Timerfunktion so angepasst:
    Weil anscheinend mit Linq die Elemente nicht gelöscht werden, habe ich jetzt ConcurrentQueue (Of T).TryDequeue verwendet

    VB.NET-Quellcode

    1. Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
    2. Dim Counter As Integer = Me._SendRecieve._AlarmQueue.Count
    3. For i = 0 To Counter
    4. Dim Alarmrow As SendRecieve._Alarmrow
    5. Me._SendRecieve._AlarmQueue.TryDequeue(Alarmrow)
    6. With Alarmrow
    7. If .State Then
    8. If Me.Alarme.Alarmtable.Rows.Count > 0 Then
    9. If Not Me.Alarme.Alarmtable.Rows.Contains(.ID) Then
    10. Me.Alarme.Alarmtable.AddAlarmtableRow(.ID, .Datetime, .Group, .Alarmtext)
    11. End If
    12. Else
    13. Me.Alarme.Alarmtable.AddAlarmtableRow(.ID, .Datetime, .Group, .Alarmtext)
    14. End If
    15. Else
    16. For Each Alarm As DataRowView In Me.AlarmtableBindingSource
    17. With CType(Alarm.Row, Alarme.AlarmtableRow)
    18. If .ID = Alarmrow.ID Then
    19. Me.Alarme.Alarmtable.RemoveAlarmtableRow(CType(Alarm.Row, Alarme.AlarmtableRow))
    20. End If
    21. End With
    22. Next
    23. End If
    24. End With
    25. Next
    26. End Sub



    Und noch eine Frage, gibt es einen Trick, dass er die neue Zeile oben ins Datatable schreibt, also so das sie oben im Datagridview angezeigt wird?

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

    zunächstmal dieses:

    Rizzle schrieb:

    Weil anscheinend mit Linq die Elemente nicht gelöscht werden, habe ich jetzt ConcurrentQueue (Of T).TryDequeue verwendet
    Ja, bei einer Queue ist das so: Enqueue(element) - element hineintun, DeQueue() - element herausnehmen.
    For Each geht die Elemente durch, entnimmt aber keines.

    Mal Vereinfachung:

    VB.NET-Quellcode

    1. Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
    2. Dim Alarmrow As _Alarmrow
    3. While Me._AlarmQueue.TryDequeue(Alarmrow)
    4. With Alarmrow
    5. Dim rw = Me.Alarme.Alarmtable.FindByID(.ID)
    6. If .State Then
    7. rw Is Nothing Then
    8. Me.Alarme.Alarmtable.AddAlarmtableRow(.ID, .Datetime, .Group, .Alarmtext)
    9. End If
    10. Else
    11. If rw IsNot Nothing Then Me.Alarme.Alarmtable.RemoveAlarmtableRow(rw)
    12. End If
    13. End With
    14. End While
    15. End Sub
    Ist wahrscheinlich nicht fehlerfrei, weil ich hab ja nicht dein Dataset, und kann hier nix testen.
    Ansonsten ist deine Benamung sehr unglücklich. "Row" würde ich nur DataRows benennen - nicht iwelche Datenstrukturen.
    Nenn es doch AlarmItem.
    Ich würde da auch eine Class für nehmen - Structures verhalten sich oft überraschend für die, die sich nicht gut damit auskennen - bzw. es sollte schon einen guten architektonischen Grund geben, eine Structure zu wählen statt einer Class.
    Nochmal mit Umbenennung:

    VB.NET-Quellcode

    1. Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
    2. Dim itm As AlarmItem
    3. While Me._AlarmQueue.TryDequeue(itm)
    4. With itm
    5. Dim rw = Me.Alarme.Alarmtable.FindByID(.ID)
    6. If .State Then
    7. If rw Is Nothing Then
    8. Me.Alarme.Alarmtable.AddAlarmtableRow(.ID, .Datetime, .Group, .Alarmtext)
    9. End If
    10. Else
    11. If rw IsNot Nothing Then Me.Alarme.Alarmtable.RemoveAlarmtableRow(rw)
    12. End If
    13. End With
    14. End While
    15. End Sub
    Jetzt ist AlarmItem klar zu unterscheiden von AlarmRow. Vorher war das extrem leicht durcheinanderzukriegen, fandich.
    Komischerweise heisst deine AlarmRow tatsächlich AlarmTableRow - auch bisserl überkandidelt - findste nicht?
    Abhilfe wäre, die AlarmTable einfach Alarm zu benennen - dasses eine DataTable ist weiss man sowieso, weil in einem Dataset ist kaum was anderes drin als DataTables.
    Hier habich eine 3-teilige Tutorial-Reihe verzapft zum Thema typDataset: codeproject.com/Articles/10309…l-Datamodel-for-Beginners
    Da verbreiter ich mich glaub in Teil2 auch über benamungs-Empfehlungen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:


    Ich
    würde da auch eine Class für nehmen - Structures verhalten sich oft
    überraschend für die, die sich nicht gut damit auskennen - bzw. es
    sollte schon einen guten architektonischen Grund geben, eine Structure
    zu wählen statt einer Class.


    Wieso wäre eine Class
    besser als ein Struct? Es ist für mich einfach nur ein Datentyp, der
    keinen Konstruktor braucht und ich keinen Speicher reservieren muss,
    somit in diesen Fall leichter anwendbar als eine Class, oder wo irre ich
    mich?

    Erstmal Danke für die Hilfe.
    Hab die Datatable jetzt
    in Alarm geändert und auch sonst deine Vorschläge übernommen, mit den
    Namen bin ich leider nicht so konsequent, deinen Artikel hatte ich
    bereits gelesen. Sodass es jetzt soaussieht:

    VB.NET-Quellcode

    1. Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
    2. Dim itm As SendRecieve._AlarmItem
    3. While Me._SendRecieve._AlarmQueue.TryDequeue(itm)
    4. With itm
    5. Dim rw = Me.Alarme.Alarm.FindByID(.ID)
    6. If .State Then
    7. If rw Is Nothing Then
    8. Me.Alarme.Alarm.AddAlarmRow(.ID, .Datetime, .Group, .Alarmtext)
    9. End If
    10. Else
    11. If rw IsNot Nothing Then Me.Alarme.Alarm.RemoveAlarmRow(rw)
    12. End If
    13. End With
    14. End While
    15. End Sub


    Ich
    habe ein extra Dataset nur dafür Erstellt, finde das gehört zu keinen
    anderen und das war das leichteste, habe ich mal angehängt. Meine
    Version lief jetzt schon über Nacht ohne Fehlermeldungen.

    Ich
    vermisse bei dem Datatable jetzt leider die funktion insert, sodass ich
    die neue Reihe immer oben einfügen kann, gibt es da eine Alternative?
    Bilder
    • Screenshot 2022-08-27 145724.jpg

      49,5 kB, 678×442, 50 mal angesehen

    Rizzle schrieb:

    Es ist für mich einfach nur ein Datentyp, der keinen Konstruktor braucht und ich keinen Speicher reservieren muss, somit in diesen Fall leichter anwendbar als eine Class, oder wo irre ich mich?
    Einen Konstruktor brauchst Du auch bei einer Klasse nicht. Dann konstruiert sich der Compiler selber zur Laufzeit einen.
    Das mit dem Speicher reservieren müsstest Du erklären. Was genau meinst Du damit? Dass Du bei einer Klasseninstanzgenerierung mit New arbeiten musst? Dann richtig. Aber es hat auch einen Nachteil eine Besonderheit, die man bei der Erstellung seiner Programme beachten muss. Eine Structure-Variable kann nie Nothing sein. Es wird immer ein Konstrukt mit Default-Werten in der Variable stecken. Eine Klassentyp-Variable kann Nothing sein, sodass sie auf keine Klasseninstanz verweist. Das kann ein Vorteil bei der Prüfung des Programmverlaufs sein. Oder für manche auch das größte Ärgernis schlechthin.
    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.

    Rizzle schrieb:

    Es ist für mich einfach nur ein Datentyp, der in diesen Fall leichter anwendbar als eine Class, oder wo irre ich mich?
    zunächstmal ist er in diesem Fall überhaupt nicht leichter anwendbar, sondern seine Anwendung ist (in diesem Fall) 100% identisch.
    Ansonsten verhalten sich Structures anders als Classes, können nicht Nothing werden wurde schon gesagt.
    Für Anfänger verheerender ist aber, dass man sie nicht wie Classes an eine Methode geben kann, die einen ihrer Werte ändert.
    probier das hier:

    VB.NET-Quellcode

    1. Public Structure ItemStruct
    2. Public ID As Integer
    3. Public Datetime As DateTime
    4. Public Group As String
    5. Public Alarmtext As String
    6. Public State As Boolean
    7. End Structure
    8. Public Class ItemClass
    9. Public ID As Integer
    10. Public Datetime As DateTime
    11. Public Group As String
    12. Public Alarmtext As String
    13. Public State As Boolean
    14. End Class
    15. Public Sub StructureTest()
    16. Dim itmStruct As ItemStruct = Nothing
    17. ChangeItemStruct(itmStruct)
    18. Debug.Print(itmStruct.Alarmtext)
    19. Dim itmClass = New ItemClass
    20. ChangeItemClass(itmClass)
    21. Debug.Print(itmClass.Alarmtext)
    22. End Sub
    23. Private Sub ChangeItemClass(itm As ItemClass)
    24. itm.Alarmtext = "ChangeItemClass"
    25. End Sub
    26. Private Sub ChangeItemStruct(itm As ItemStruct)
    27. itm.Alarmtext = "ChangeItemStruct"
    28. End Sub
    ChangeItemClass() geht, ChangeItemStruct() nicht.

    Ich bin zu faul, die genauen Hintergründe zu erklären - wenndes wissen willst, lade dir ein gutes Online-Buch runter, von Löffelmann oder von Kühnel/Leibhard. Aber keinesfalls von Theiss - der hat keine Ahnung.
    Da lies dir das Kapitel zu WertTyp/ReferenzTyp durch.
    Kannst auch im Netz herumgoogeln, aber da weiss man nie, an wen man gerät, am Ende landest du bei einer uTube-Koryphäe (dannlieberdoch Theiss).

    Jedenfalls Structures sind fast immer äusserst primitive Typen. Integer ist zB ein Struct. Oder DateTime.

    Gewöhn dir nicht an, Datentypen als Structure zu entwerfen, und dann auf Schnauze fallen (s. ChangeItemStruct()), wenn du wirklich mit denen arbeiten willst.
    zunächstmal ist er in diesem Fall überhaupt nicht leichter anwendbar, sondern seine Anwendung ist (in diesem Fall) 100% identisch.


    Dann wäre der Anwendungsfall hier ja ok?

    Dass Structs nicht Nothing werden können, habe ich nie beachtet, das man sie nicht als Variable an Methoden geben kann, war mir aber bewusst.
    Eigentlich nutze ich Structes nur mit Dics, da finde ich sie Praktisch.
    Das ist aber noch eine Sache aus dem Studium bei der hardwarenahen Programmierung mit C, da gab es nur Structs und keine Klassen. Das soll aber hier keine Ausrede sein, nehme gerne konstruktive Kritik an.

    Der Link von VaporiZed ist auch interssant.

    Ich habe außerdem die Bücher von Walter Doberenz, sind die auch Murks in euren Augen?

    Rizzle schrieb:

    Dann wäre der Anwendungsfall hier ja ok?
    Also es funktioniert erstmal. Die Gefahr besteht eher darin, dass du Structs öfter mal benutzt, und iwann damit auf Nase fällst. Aber du scheinst dich ja besser auszukennen, als ich dachte.
    Aber deine C-Vorbildung erklärt deine (für uns eigenartige) Annahme, Structs seien einfacher als Classes. In C ist das so, in vb.net nicht.
    Bei Doberenz mussich passen - ich weiss, dass er Autor ist, hab aber nichts von ihm gelesen. Allerdings auch nichts schlechtes von ihm gehört.

    Den Link-Inhalt von VaporiZed habich nicht verstanden. Wortreich wird dort Nothing gebant, aber was das eiglich schlimmes tut, ausser des Autors "only design rule" zu breaken ("bijection" - whatever that means), habich nicht geschnallt.
    Null steht für mich für die Abwesenheit von etwas, und das kommt mir sinnvoll vor.
    Es kommen Leute aussm Krieg, denen fehlt der rechte Arm. Händeschütteln mit dene muss halt fehlschlagen.

    ErfinderDesRades schrieb:

    Händeschütteln mit dene muss halt fehlschlagen.
    In manchen Fällen muss das aber eben nicht bedeuten, dass die App crasht, wenn man es nicht prüft, ob eine rechte Hand da ist, wenn man Hände schütteln will, sondern es bedeutet, dass die andere Hand hergenommen wird oder beim Versuch eben gar nichts passiert. Das Prüfen auf null/Nothing soll damit in vielen Fällen überflüssig werden.
    Disclaimer, falls allein schon die Erwähnung dieses Prinzips von irgendjemandem gleich so gewertet wird, als ob ich absolut davon überzeugt wär und jetzt jeden missionieren will, das in allen Bereichen umzusetzen: Ich habe es als Denkanregung erwähnt.
    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:

    In manchen Fällen...
    Ja, in manchen Fällen.
    Und wenn man so einen Fall haben will, muss man entsprechend was programmieren.
    Zunächst abermal, und ohne besondere Vorkehrungen, musses crashen - so früh wie möglich, um Folgefehler zu vermeiden, denen ja viel schwieriger auf die Schliche zu kommen ist.

    (Also Denkanregung hat funktioniert - und meine Denke erweist sich halt als höchst unüberzeugt)