Datagrid und System.Threading.Timer -> keine Aktualisierung der Anzeige?

  • VB.NET

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

    Datagrid und System.Threading.Timer -> keine Aktualisierung der Anzeige?

    Hallo

    Ich habe heute erste Experimente mit dem System.Threading.Timer durchgeführt. Dieser sollte eine Methode in einer Klasse aufrufen, welche eine neue Datarow mit werten füllt und zur entsprechenden Datatable hinzufügt. Mit der Datatable ist ein Datagrit verbunden in dem ich mir die Werte dann anzeigen lassen möchte. Rufe ich diese Methode "händisch" z.B. mittels eines Buttons auf, werden die Werte fein in das Grid eingetragen und alles funktioniert wie es soll. Verwende ich aber den Timer passiert scheinbar nichts. Scheinbar, weil in Wirklichkeit doch etwas passiert aber das Steuerelement nicht automatisch aktualisiert wird. Klicke ich auf den Header des Grids erscheinen plötzlich alle werte die bis dahin eingelesen wurden. Eine weitere Aktualisierung findet aber auch dann nicht statt.

    Nun Frage ich mich ob und wenn ja wie ich das umgehen kann. Gibt es da eine Möglichkeit? Ach ja, ein Reset der Bindingsource hatte leider auch keinen effekt:/.

    metzelmax schrieb:

    Dieser sollte eine Methode in einer Klasse aufrufen, welche eine neue Datarow mit werten füllt und zur entsprechenden Datatable hinzufügt.
    ...wird er wahrscheinlich nicht machen, prüfe mit einer MessageBox, ob er diese Routine wirklich startet.
    Dafür solltest Du einen Form.Timer nehmen, denn mit dem Threading.Timer musst Du invoken.
    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!
    @VB1963: Die Routine wird schon angesprungen. hab ich eben nochmal geprüft.

    @Rod: Danke für den Tip. Das mit dem Invoke hab ich fast befürchtet. Anders als bei dem schreiben von Werten in ein Label, Was ich mittels me.Invoke(Sub() ...) erledige handelt es sich hierbei aber um eine Datenbindung... Da weiß ich wirklich nicht weiter. Dachte bisher, das die Bindung nur ein Verweis ist und es egal ist, welcher Thread in die Datatable Daten packt. Scheinbar hab ich mich da getäuscht...

    Gibt es eine Möglichkeit diesen Threadübergreifenden Zugriff per Threading.Timer auf die Datatable und das Datagridview zu ermöglichen?
    Ja, hab ich an sich auch verstanden, nur nicht wie...

    Wie man aus einem Thread auf Steuerelemente zugreift weiß ich an sich. Aber hier schreibe ich ja nicht direkt in das Datagrid sondern habe vorher einmalig ein Databinding durchgeführt mit der Datatable. Wo und wie ich da invoken soll/kann weiß ich leider nicht... Aber wenn du es wissen solltest nur her mit den Infos ;).

    Hier mal ein Auszug wie ich das mache:

    VB.NET-Quellcode

    1. DGV_Messwerte.DataSource = MyZR5.Messdaten 'Datenbindung im Programm
    2. Chart_Messwerte.DataSource = MyZR5.Messdaten
    3. Public Property MessStart As Boolean 'Property in einer separaten Klasse in welcher auch die Werte in die Datatable gefüllt werden soll
    4. Get
    5. Return _MessStart
    6. End Get
    7. Set(value As Boolean)
    8. If value Then
    9. MessTimer = New System.Threading.Timer(AddressOf Messwert_Logging, Nothing, 0, MessIntervall * 1000)
    10. Else
    11. MessTimer.Dispose()
    12. End If
    13. _MessStart = value
    14. End Set
    15. End Property
    16. Public Sub Messwert_Logging(ByVal State As Object) 'Sub zum füllen der Datatable
    17. Dim Row_ZR5 As MyDataSet.Dt_ZR_5_MWRow = _messTable.NewDt_ZR_5_MWRow
    18. Row_ZR5.Time = CDate(DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss"))
    19. Row_ZR5.O2 = _o2Value
    20. Row_ZR5.T_Zelle = _temperatur
    21. Row_ZR5.U = _spannung
    22. Row_ZR5.status = ([Enum].GetName(GetType(OnOff), _Status))
    23. Row_ZR5.Volumen = _volumen
    24. _messTable.AddDt_ZR_5_MWRow(Row_ZR5)
    25. _messTable.AcceptChanges()
    26. End Sub

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

    Probier mal:

    VB.NET-Quellcode

    1. Me.Invoke(Sub()
    2. _messTable.AddDt_ZR_5_MWRow(Row_ZR5)
    3. _messTable.AcceptChanges()
    4. End Sub)
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    @spaceyx

    Das geht leider nicht, da diese beiden Befehle sich in der Klasse befinden und vom Timer aufgerufen werden. Invoke ist da nicht möglich.

    Ah, also sollte das prinzipiell funktionieren? Na das klingt ja schonmal fein:).

    @Erfinder:
    Ich nutze die Win-Forms Bindingsource-komponente. Entsprechend wird der meiste Code im Designer erzeugt. Beim Datagridview habe ich das Binding mittels des Assistenten über Objekt erzeugt und dann die entsprechende Datatable darin gewählt (is ne dll Datei). Die Datatable ist in der Klasse MyDataSet enthalten welche sich in der MyClasses-dll befindet. Die Klasse in welcher ich die Datatable mittels Timer füllen möchte besitzt eine eigene Instanz der Mydataset-Klasse und gibt die Datatabel via Property an meine Forms-Anwendung zurück wo ich Sie mit Datagrid und Chart verknote. Übrigends ist das Verhalten beim Chart genauso. Es zeigt erst etwas an wenn es neu skaliert wird...

    Ich hab mal versucht die entsprechenden Verweise im Designer zu finden, vielleicht hilft das ja was:

    VB.NET-Quellcode

    1. 'Databinding
    2. Me.DtZR5MWDataTableBindingSource = New System.Windows.Forms.BindingSource(Me.components)
    3. Me.Chart_Messwerte.DataSource = Me.DtZR5MWDataTableBindingSource
    4. Me.DGV_Messwerte.DataSource = Me.DtZR5MWDataTableBindingSource
    5. Me.DtZR5MWDataTableBindingSource.DataSource = GetType(MyClasses.MyDataSet.Dt_ZR_5_MWDataTable)
    6. CType(Me.DtZR5MWDataTableBindingSource, System.ComponentModel.ISupportInitialize).EndInit()
    7. 'Datasource
    8. Me.Chart_Messwerte.DataSource = Me.DtZR5MWDataTableBindingSource
    9. Me.DGV_Messwerte.DataSource = Me.DtZR5MWDataTableBindingSource
    10. Me.DtZR5MWDataTableBindingSource.DataSource = GetType(MyClasses.MyDataSet.Dt_ZR_5_MWDataTable)
    11. 'Im Forms-Code
    12. DGV_Messwerte.DataSource = MyZR5.Messdaten 'Datenbindung
    13. Chart_Messwerte.DataSource = MyZR5.Messdaten
    14. 'MyZR5 ist eine Instanz der Klasse, welche die Datatable über die Property Messdaten verfügbar macht


    Macht es einen unterschied woher ich die Daten erhalte? Das Dataset befindet sich genau wie die Klasse in der es gefüllt wird in einer dll welche im Projekt eingebunden wurde.

    metzelmax schrieb:

    Die Klasse in welcher ich die Datatable mittels Timer füllen möchte besitzt eine eigene Instanz der Mydataset-Klasse
    Dassis ein architektonischer Fehler.
    Es darf keine 2 Mydatasetse geben.
    Du sagst zwar was von Property und im Form verknoten, aber es ist eher unwahrscheinlich, dass dir das richtig gelingt.
    Nein, die Klasse muß ein Event senden, Und zwar mittels Application.OpenForms(0).BeginInvoke() muß dieses Event in den MainThread verlagert sein.
    Im Form im MainThread holt sich das Form die Daten aus der Klasse und fügt sie als Datensätze ins MyDataset ein. (So ist dann auch kein BindingSource.ResetBindings nötig).

    Dieses erstmal mittm DGV ans laufen bringen - Chart kommt später.
    Hallo,

    naja, prinzipiell gibt es ja auch nur ein Dataset, eben mit verschiedenen Verweisen auf selbiges. Ob ich das aber richtig verknotet habe, darüber kann ich mit meinen noch geringen Kenntnissen freilich wenig sagen... Aber das füllen des Datasets mittels Forms-Timer funktioniert zumindest ohne Probleme:

    VB.NET-Quellcode

    1. Dim WithEvents MyTimer As New Timer
    2. Private Sub Btn_Test_Click(sender As Object, e As EventArgs) Handles Btn_Test.Click
    3. MyTimer.Interval = 1000
    4. MyTimer.Enabled = Not MyTimer.Enabled
    5. End Sub
    6. Private Sub MyTimer_Tick(sender As Object, e As EventArgs) Handles MyTimer.Tick
    7. MyZR5.Messwert_Logging(Nothing)
    8. End Sub


    Daher dachte ich vermutlich auch das das ganze so funktionieren sollte...

    Gut, aber nun zu deinem Vorschlag. Woher bekomme ich das Event? Muss ich das selbst erstellen oder existiert selbiges im .net? Und wie funktioniert das Application.OpenForms(0).BeginInvoke()? Wann sollte das aufgerufen werden, und wo? Nach dem Accept Changes beim Füllen der Datatable?
    Der Heini ist ein Programmierer im Institut :D . Er meinte das man nachdem man die Werte in die Table geschrieben hat, diese mit AcceptChanges speichern sollte. Naja und ich vertrau mal drauf, dass das nicht verkehrt ist. Bisher funktionierte das ganze ja auch.

    Danke erst mal für den Hinweis mit dem BeginnInvoke. Hab ich gleich eingebaut und funktioniert zumindest bei den Labels gut. Das mit dem Grid habe ich aber noch nicht hinbekommen. Zudem scheine ich ein Problem mit dem Databinding des Charts zu haben. Benötige ich für jede Komponente (also Chart und Grid) eine eigene Bindingsource oder können beide auf die gleiche verweisen. Ich frage weil folgendes nicht so recht funktionieren möchte:

    VB.NET-Quellcode

    1. 'Datanquelle der Bindingsource neu zuweisen (Bindingsource sowohl im Grid als auch im Chart verwendet)
    2. DtZR5MWDataTableBindingSource.DataSource = MyZR5.Messdaten
    3. 'Grid funktioniert danach wie zuvor. Das Chart macht aber gar nix mehr (auch nicht beim neu skalieren)
    4. 'bei direktem Zuweisen funktioniert das Grid und das Chart (allerdings wird das Chart nicht automatisch aktualisiert)
    5. DGV_Messwerte.DataSource = MyZR5.Messdaten
    6. Chart_Messwerte.DataSource = MyZR5.Messdaten


    Das Chart scheint selbst recht eigensinnig zu sein. Die Series sind konfiguriert und die Werte konnten auch entsprechend der Bindingsource zugewiesen werden. Im Programm tut sich aber ohne skallieren der Form gar nichts. Und auch dann wird das Diagramm scheinbar nur einmal aktualisiert... Hat vielleicht jemand ne Erklärung dafür?


    Ach ja, ich hab auch eine Warnung bekommen:

    Warnung 1:
    Das urn:schemas-microsoft-com:xml-msdatasource:GenericObjectDataSource-Element wurde nicht deklariert. C:\...\ZR5_Control\ZR5_Control\MyProject\DataSources\MyClasses.MyDataSet+Dt_ZR_5_MWDataTable.datasource

    VB.NET-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <!--
    3. This file is automatically generated by Visual Studio .Net. It is
    4. used to store generic object data source configuration information.
    5. Renaming the file extension or editing the content of this file may
    6. cause the file to be unrecognizable by the program.
    7. -->
    8. <GenericObjectDataSource DisplayName="MyDataSet+Dt_ZR_5_MWDataTable" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
    9. <TypeInfo>MyClasses.MyDataSet+Dt_ZR_5_MWDataTable, MyClasses, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
    10. </GenericObjectDataSource>
    11. ' Wobei das "GenericObjectDataSource" blau unterringelt ist

    metzelmax schrieb:

    Der Heini ist ein Programmierer im Institut :D .
    Warum fragst Du nicht ihn selbst?
    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!
    @ ROd: Naja, das hab ich schon getan und er wusste auch nicht so recht woran das liegt, weil er seine Daten anders organisiert. Naja, und heut gönn ich mir mal einen Tag "Heimarbeit" sodass ich hoffe einer hier im Forum kann mir vielleicht weiterhelfen:).

    @sonne75: Danke für den Link
    So, das mit dem Chart habe ich jetzt erst einmal behelfsmäßig hinbekommen. Dabei reicht auch eine Bindingsource. Ich habe einfach in die Routine welche durch den Windows Forms Timer aufgerufen wird ein Chart_Messwerte.DataBind() eingefügt. Dadurch wird das Chart manuell aktualisiert. Das funktioniert auch scheinbar ganz gut. Ist das immer erforderlich? (Habe vorher nie mit dem Chart gearbeitet...)

    Leider ist damit aber das eigentliche Problemchen noch nicht gelöst, die Verwendung eines Threading.Timer zur Aktualisierung der Datatable und dem Anzeige der Werte im Datagrid sowie Chart. Wie gesagt, manuell über Aufruf der Methode welche der Threading.Timer nutzt funktioniert alles einwandfrei. Mit dem Threading-Timer erfolgt jedoch keine Aktualisierung der Steuerelemente...
    zu acceptChanges: [VB 2010] Access Datenbank bearbeiten




    metzelmax schrieb:

    Chart_Messwerte.DataBind() eingefügt. ... Ist das immer erforderlich?
    Ja - siehe ChartTester
    In diesem Punkt unterscheidet sich Databinding beim DGV vom Chart.




    zum eiglichen Problemchen steht doch ein LösungsAnsatz in post#10. Wenn das nicht funzt, hastes wohl falsch (oder ühaupt nicht) umgesetzt.
    Danke für die Links. Das Chart hatte ich mir schon angesehen und runtergeladen. Da waren schon ein paar nützliche Infos bei:). Den anderen Link acker ich gleich durch.

    Nochmal zu der Warnung weiter oben: Kann man die getrost ignorieren?

    metzelmax schrieb:

    zu der Warnung weiter oben
    Welche war das doch gleich?
    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!