Datagridview per Thread aktualisieren

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von Radinator.

    Datagridview per Thread aktualisieren

    Huhu liebe Community,

    ich habe versucht, per neuem Thread eine Datagridview zu füllen. Leider aktualisiert sich der Control nicht, auch wenn im Hintergrund die Datagridview trotz neuem Thread gefüllt wird.

    Ich habe versucht, mittels Datagridview1.refresh() zumindest die einzelnen Einträge zu sammeln, jedoch bekomme ich eine Exceptionfehler:

    Zusätzliche Informationen: Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement DataGridView1 erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. test= New System.Threading.Thread(AddressOf Me.fuelledatagrid)
    3. test.IsBackground = True
    4. test.Start()
    5. End Sub
    6. Sub fuelledatagrid()
    7. [...] ' Sammle hier Daten
    8. Datagridview1.refresh()
    9. End Sub


    Wie kann ich dieses Problemchen am einfachsten lösen? :(

    Vielen Dank und mit freundlichem Gruß
    Das geht so nicht. Es ist, wie die Exception sagt: ein Dgv kannste nicht aus einem Nebenthread befüllen.

    Du musst das trennen:
    1. Datensammeln im Nebenthread
    2. wechseln in MainThread, und dort das Dgv füllen. hierzu gugge Controls Threadsicher machen
    Beachte: Invoking ist teuer, also invoke nicht für jede zeile, sondern wie gesagt: 1) Daten sammeln 2) Dgv updaten, mit alle Daten auf einmal.

    Semiconductor schrieb:

    im Hauptthread
    ist das zu tun, damit wird sichergestellt, dass niemals zwei Threads gleichzeitig insbesondere schreibend auf ein GUI-Control zugreifen können.
    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!

    Semiconductor schrieb:

    Das nacheinanderfüllen, also quasi in Laufzeit ist also dann im Hauptthread zu machen?

    Das "alles auf einmal füllen" ist in meinem Falle unschön/ungünstig.
    Wenn du das nicht willst, dann lass das Threading besser weg.
    Ein Thread, der nix tut, als in den MainThread zu delegieren, tut nix, ausser Performance fressen.
    Hi @Semiconductor:

    Je nach Menge der Daten kannst du auch versuchen das ganze mit Timer und einem eigenen Event zu lösen.
    Dazu einen Timer abstellen, der alle - meinetwegen - 5 Sekunden tickt und die Daten besorgt. Nachdem alle benötigten Daten vorliegen dann ein Event feuern, welches die Daten zurück übergibt, um sie dann in das Controls bzw die Cells zu schreiben.

    Hab für eines meiner letzten Projekte sowas gebrauchen können und hoffe, dass ich dir damit auch helfen kann :D

    Lg Radinator

    PS: Sowohl das Abholen der Daten, als auch das Eintragen kann bei Bedarf in einen Task ausgelagert werden.
    EDIT: Falls du ein bisschen mit UML umgehen kannst, siehe Anhang
    Bilder
    • dgv_updaten.png

      13,77 kB, 540×440, 221 mal angesehen
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

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

    Okay, ich könnte dadurch zwar das Threadding umgehend, aber ich habe das Problem, dass der User zur Laufzeit quasi verschiedene Datentables hat, die er zeitgleich "laufen" lassen kann, oder nicht.

    Da ich nicht weiß, wieviele Timer ich benötigen müsste, müssten Timer zur Laufzeit erstellt werden. Da habe ich mich nicht sonderlich schlau gemacht und müsste mich erstmal einlesen.
    ich versteh immer weniger.
    Der user kann mehrere DataTables gleichzeitig laufen lassen?
    Aber die sollen schon in dem Dgv angezeigt werden - mehrere DataTables gleichzeitig, oder wie ist das gedacht?

    Vielleicht erklärst du mal, was das ganze eiglich soll.
    Also erzähl nix von DataTables oder Dgvs, sondern was zeigt das Proggi an, und was kann/soll der User damit tun?

    Semiconductor schrieb:

    wieviele Timer
    Offensichtlich genau so viele wie Threads.
    Vielleicht machst Du zunächst mal einen "Generalplan" des Ablaufes, und dann überlegen wir, wie die Aufgaben verteilt und organisiert werden.
    ----
    Was spricht gegen eine Prozessierung in Threads, wenn Du nur bei jedem zehnten Wert oder so eine GUI-Ausgabe anstößt?
    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!

    Semiconductor schrieb:

    Da ich nicht weiß, wieviele Timer ich benötigen müsste, müssten Timer zur Laufzeit erstellt werden.
    Nimmste eine List(Of Timer) und für jedes DGV das du erzeugst, erstellst du einen extra Timer, registrierst erst einen Eventhandler für das TickEvent und wirfst ihn dann in die Liste mit dazu.
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Der Ideengang:

    Controls:
    *Eine Datagridview
    *Links eine Listbox mit mehreren Einträgen.


    Hinter jedem Listboxeintrag existiert eine eigene datatable. Diese datatable wird von einer Datei erzeugt.
    Mache ich einen Doppelclick auf einen Listboxeintrag, soll in einem zyklischen Verlauf (alle 60 Sekunden) die datatable aktualisiert werden und auf die Datagridview geschoben werden.

    Klicke ich nun einen anderen Listboxeintrag, soll die Datagridview nun auf die datatable des angeklickten Eintrages switchen.

    Der vorherige angeklickte Eintrag läuft im Background weiter, klickt man diesen vorherigen Eintrag von der Listbox an, soll die Datagridview die Daten der laufend aktualisierenden datatable anzeigen.

    Ist eigentlich nicht komplex, mein Schreiben mag vermutlich so erscheinen ^^"

    Mein Fortschritt: Ich klick auf Item 1, Datatable aktualisiert sich, datagridview zeigt es an.
    Ich klick auf Item 2, Datatable aktualisiert sich, datagridview zeigt auch dies erfolgreich an.
    Klicke ich wieder auf Item 1 zurück, so wird die datagridview auf item 1 eben aktualisiert.

    Mir fehlt nun lediglich die Abfrage alle 60 Sekunden und dass ich trotz der Abfrage Zugriff auf die datatables mittels Datagridview habe.

    Die Anzahl der Einträge ist nicht determiniert, d.h., ich kann nicht vorher schreiben Dim thread1... etc.

    Das muss, wie bereits netterweise erwähnt, zur Laufzeit erzeugt werden.

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

    Semiconductor schrieb:

    läuft im Background weiter
    heißt letzten Endes, dass Du permanent alle Tabellen vorhältst.
    Wenn die Daten in einer dynamischen Datei liegen, solltest Du mit nem FileSystemWatcher auf Änderungen dieser Dateien reagieren und sie bei Bedarf neu einlesen.
    Wenn es nicht zu viele Dateien sind, mach Dir ein TabControl, wo Du auf jeder Seite eine eigene Tabelle darstellst, in dwen Vordergrund geholt durch einen Klick auf den Reiter. Da kann die ListBox entfallen.
    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!
    @ErfinderdesRadesnull
    Was zeigt das Programm an?
    Eine XML Datei durch die Control Datagridview mittels einer datatable.

    Was soll der User tun?
    Auf einen Button klicken, der die XML Datei laufend aktualisiert.
    Klickt der User auf einen zweiten Listboxeintrag, der eine andere XML Datei anzeigt, soll dann diese angezeigt werden, während XML-Datei1 im Hintergrund weiter aktualisiert wird.

    Was kann der User damit tun?
    Aufbereitete XML Daten in RealTime einsehen ohne auf F5 oder sonstwas zu klicken und statistisch auswerten.

    @Rod
    Ich dachte auch an eine Tabcontrol, gibt es eine schöne Lektüre zur Laufzeiterzeugung von Tabcontrols/DGVs allgemein? Ich müsste mich da erstmal schlau lesen!

    Semiconductor schrieb:

    Laufzeiterzeugung von Tabcontrols
    Das ist ganz simpel.
    Du machst Dir ein UserControl mit dem DGV und was Du sonst noch brauchst.
    Dann erstellst Du Dir das TabControl mit Deinem UserControl auf der 1. Seite und schreibst den relevanten Code aus der Prozedur InitializeComponent() in der Datei FormX.Designer.vb ab:

    VB.NET-Quellcode

    1. Private nummer As Integer = 0
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim tp = New TabPage
    4. Dim uc = New MyTabPageContent ' Dein Control halt
    5. tp.Controls.Add(uc)
    6. tp.Text = nummer.ToString
    7. nummer += 1
    8. Me.TabControl1.TabPages.Add(tp)
    9. End Sub
    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!
    Update:

    Ich habe nun durch Recherchen auf jeden Fall die Laufzeitgenerierung von Tabcontrols, Datagridviews und vor allem datatables verstanden, was auf jeden Fall sehr gut ist! Vielen Dank nochmals dafür Rod!

    Da ich in Form von Threading etwas ausprobieren wollte und mich bereits durchgelesen habe, habe ich das grobe Konstrukt von Multithreading verstanden.

    Die Nutzung von Invoke aus einem Thread auf eine Control ist so am besten gesehen möglich. Leider finde ich da zumindest keine guten Erklärungen für Invoke auf verschiedenste Beispielcontrols in Form von expliziten Beispielen.


    Hier ein fehlerhafter Code, der eine Threadübergreifende Exception verursacht. Ich habe das jetzt exemplarisch verfasst, um zu erklären, was meine Herangehensweise war, bevor ich die Funktion und Nutzung von Invoke gelernt habe.

    VB.NET-Quellcode

    1. [...]
    2. Sub NeuerlaufzeitThread(ByVal test As Integer)
    3. Do
    4. datatable(test).Rows.Add("TEST", "TEST2")
    5. Threads(counter).Sleep(750)
    6. Loop Until stopp = false
    7. Threads(counter).Abort()
    8. End Sub


    Wie füge ich hier das Invoke am besten und einfachsten ein?

    datatable ist gebunden an eine dynamisch erzeugte datagridview

    VB.NET-Quellcode

    1. ​Invoke(Sub() datatable(test).Rows.Add(...))
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Vielen Dank :D

    Ich habe gedacht, eine Delegate wäre Pflicht, aber dieser Invoke hat auch gereicht! Endlich verstehe ich nun die Basics des Multithreadings. Ich wollte nämlich den normalerweise einfachen Weg mit


    VB.NET-Quellcode

    1. Control.CheckForIllegalCrossThreadCalls = False


    umgehen, da der ja keineswegs eine Empfehlung ist ;)

    MfG
    Semi
    Du kannst anstatt der anonymen Methode auch einen Delegaten verwenden(parameterloße Methode erstellen und in den Namen der Invoke übergeben
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell