DataGridView aus Thread aktualisieren

  • VB.NET

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    DataGridView aus Thread aktualisieren

    Edit by ErfinderDesRades: (Thema verschoben) Bitte richtiges UnterForum wählen!


    Hallo Zusammen,

    bei meinem Programm wird ein Thread gestartet, welcher Daten verarbeitet. Die Daten aus dem Thread sollen nun in ein DataGridView, welches sich auf der Form befindet, gebracht werden.

    Folgender Code, den im Designer erstellten DataTable mit den Daten des Threads befüllen:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. For Each _Character As Character In _CharacterList
    2. Dim _NewRow As dsResults.dtCharactersRow = fmMain.DsResults.dtCharacters.NewdtCharactersRow
    3. With _NewRow
    4. .colName = _Character.Name
    5. .colGUID = _Character.GUID
    6. .colAccountID = _Character.AccountID
    7. .colKnownTitles = _Character.BitmaskBackup
    8. '.colAffected =
    9. '.colLeft =
    10. '.colRemoved =
    11. End With
    12. fmMain.DsResults.dtCharacters.Rows.Add(_NewRow)
    13. Next
    14. Dim _update As New ControlDataGridViewUpdater(fmMain.dgvCharacters)
    15. _update.SetNewDT(fmMain.DsResults.dtCharacters)

    Den DataTable hatte ich im Designer erstellt, weil es sonst recht viel Code ist den DataTable so zu erzeugen.
    Ich bin mir fast sicher, dass dies unter Umständen anders zu handeln ist. Naja, jedenfalls wird der DataTable befüllt und dann der UpdateProzess für das DataGridView aufgerufen.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Explicit On
    2. Option Strict On
    3. Public Class ControlDataGridViewUpdater
    4. Private _dgv As DataGridView
    5. Private _dt As DataTable
    6. Public Sub New(dgv As DataGridView)
    7. _dgv = dgv
    8. End Sub
    9. Private ReadOnly Property P_dgv As DataGridView
    10. Get
    11. Return _dgv
    12. End Get
    13. End Property
    14. Public Sub SetNewDT(dt As DataTable)
    15. SyncLock Me
    16. _dt = dt
    17. P_dgv.Invoke(New MethodInvoker(AddressOf ThreadSaveSet)) '// InvalidOperationException - "Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde."
    18. End SyncLock
    19. End Sub
    20. Private Sub ThreadSaveSet()
    21. Me.P_dgv.DataSource = _dt
    22. End Sub
    23. End Class

    Hier komme ich dann bei dem oben markierten Punkte nicht wirklich weiter. Wie genau würdet Ihr ein beliebiges DataGridView aus einem Thread aktualisieren?

    Gruß, FireEmerald

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

    @FireEmerald:: Das Zauberwort heißt Invoke.

    VB.NET-Quellcode

    1. dlg.Invoke(Sub() BitteAktualisiereMeinDgv())
    dlg ist die Form mit dem DGV, BitteAktualisiereMeinDgv() ist die Sub, in der die Aktualisierung vorgenommen wird.
    Feddich.
    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 Frage ist, was muss in dem BitteAktualisiereMeinDgv() Sub genau aktualisiert werden und was kann außerhalb aktualisiert werden.

    Reicht es aus die BindingSource in dem Sub neu zuzuweisen oder muss die Befüllung des DataTables auch dort stattfinden? Wenn ich aus dem mit Invoke aufgerufenen Sub weitere Subs aufrufe, sind diese ja wiederum nicht Invoked?

    Ich bekomme nach wie vor den selben Fehler.

    VB.NET-Quellcode

    1. fmMain.Invoke(Sub() test())
    2. '// Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde.
    3. Private Sub test()
    4. fmMain.dgvCharacters.DataSource = fmMain.DsResults.dtCharacters
    5. End Sub

    Wenn ich das ganze ohne Invoke probiere, kommt zwar kein Fehler, aber das DGV bleibt auch leer, obwohl ich ihm ein DataTable zuweise, welches Daten enthält. Aus dem Grund versuche ich es gerade mit Invoke, um das DGV einen DT zuzuweisen.

    VB.NET-Quellcode

    1. Dim dt As New DataTable With {.TableName = "mytable"}
    2. dt.Columns.Add("col1")
    3. dt.Columns.Add("col2")
    4. dt.Rows.Add("1", "2")
    5. With fmMain.dgvCharacters
    6. .DataSource = dt
    7. End With

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

    FireEmerald schrieb:

    VB.NET-Quellcode

    1. Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde.
    Lies Dir mal diese Fehlermeldung genau durch.
    In welchem Fenster befondet sich das DataGridView?
    In welcher Prozedur rufst Du Invoke() auf?
    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!
    Das DataGridView befindet sich in der fmMain (Windows Form).
    Aus der fmMain wird ein Thread gestartet, welcher eine Sub Remove aus einer Cls_Main ausführt.

    Das Sub Remove wird also in einem neuen Thread ausgeführt.

    Aus diesem Sub wird wiederum eine New Cls_dgvCharacters(_CheckedCharacterList) aufgerufen und an diese die _CheckedCharacterList Variable übergeben (eine ListOf mit Daten).
    Nach dem erstellen der Cls_dgvCharacters Klasse, wird ein Sub in dieser aufgerufen Sub Initialize(). In diesem Sub findet dann folgendes statt:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim dt As New DataTable With {.TableName = "mytable"}
    2. dt.Columns.Add("col1")
    3. dt.Columns.Add("col2")
    4. dt.Rows.Add("1", "2")
    5. With fmMain.dgvCharacters
    6. .DataSource = dt
    7. End With

    Und in genau dieser Sub Initialize() rief ich auch das fmMain.Invoke(Sub() test()) auf. Die Sub test befand sich in der selben Klasse von der Sie aufgerufen wurde.
    @FireEmerald:: Das ist etwas unklar.
    Kannst Du vllt. den ganzen Code posten?
    Falls das Projekt zu groß ist, mach ggf. ein ganz kleines Testprogramm, das genau diesen Effekt reproduziert.
    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!
    @RodFromGermany Habe mir mal die Mühe gemacht das ganze in ein kleines Testprojekt einzubauen. Hoffe du kannst es so besser nachvollziehen.

    <siehe unten>

    Wie du siehst, wird in dem DGV nichts dargestellt...

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

    FireEmerald schrieb:

    Hoffe du kannst es so besser nachvollziehen.
    Noch nicht.
    Was soll ühaupt passieren?
    Hast Du eine funktionierende Lösung, die im Main-Thread arbeitet?
    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!
    Ok, ich hab zwei Buttons hinzugefügt, was funktioniert und was nicht: <siehe unten> bzw. wie es sein sollte.
    Das wo passiert, wenn du auf den Button "Funktioniert" klickst, sollte auch passieren, wenn du auf den Button "Funktioniert nicht" klickst.

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

    FireEmerald schrieb:

    Zumindest von der "Instanziierung von Forms"
    Ganz falsch, denn dann hast Du ja 2 Instanzen Deiner Hauptfenster-Klasse.
    Besser ist es, die Daten bereitzustellen und ein Event aufzurufen, dass die Daten fertig sind, dann kann sich das Hauptfenster allein drum kümmern.
    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!
    @RodFromGermany, soll heißen der Invoke muss immer zwingend über die Form gehen, auf welcher sich das DataGridView befindet, da sich dort nunmal der Fensterhandle für das DataGridView befindet?

    Hab ein paar Änderungen durchgeführt, wäre nett wenn du es dir nochmal anschauen könntest, ob es noch was zu verbessern gibt oder was wo dir auffällt:
    DataGridView_Testprojekt_Threading_RE4.rar

    Funktioniert wie es soll.
    Datasets kann man im NebenThread befüllen (oder sonstwas machen), wenn man zuvor alle anhängigen BindingSources disabled hat

    VB.NET-Quellcode

    1. BindingSource.RaiseListChangedEvents=False
    2. 're-enablen
    3. BindingSource.RaiseListChangedEvents=True
    4. BindingSource.ResetBindings(False)