Multithreading in der WPF

  • WPF MVVM
  • .NET (FX) 4.5–4.8

Es gibt 29 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    Multithreading in der WPF

    Hallo und guten Abend :)

    Ich bin mal wieder beim Übersetzen von WinForms Code in "WPF-taugliche" Befehle.

    Bisher hatte ich Folgendes in WinForms:

    VB.NET-Quellcode

    1. Private Sub OnTrackEnd(syncHandle As Integer, channel As Integer, data As Integer, user As IntPtr)
    2. Me.Invoke(Sub() Me.TrackEnd())
    3. End Sub


    Im WPF-Projekt habe ich bis jetzt Folgendes:

    VB.NET-Quellcode

    1. Private MeinDispatcher As Dispatcher
    2. MeinDispatcher.Invoke(AddressOf TrackBeendet)


    Kommt aber immer der Fehler, dass der Objektverweis nicht auf eine Objektinstanz festgelegt wurde.

    IntelliSense zeigt mir hier 17 Überladungen an, aber aus den MS Docs werd ich nicht wirklich schlau...

    Kann mir jemand unter die Arme greifen, das ist das erste Mal, dass ich mit Mutlithreading arbeite... Bin ich überhaupt an der ricthigen Methode dran (.Invoke)?

    Ihr braucht bestimmt noch mehr Code, bitte kurz fragen was genau...

    Grüssle, kafffee
    Hab jetzt mal einen WPF Crash-Kurs versucht, dachte könnte SingleTon sein, wo man einfach die Instanz über eine statische Funktion holt, aber Dispatcher selbst scheint statisch zu sein, als ich mit Intellisense nach einer Funktion suchte um eine Instanz zu bekommen, viel mir auf, das direkt Invoke vorgeschlagen wird, daraufhin habe ich das probiert und ohne Crash ging es, ob das nun nach MVVM ist keine Ahnung :whistling:

    Vllt. kommste damit weiter.

    VB.NET-Quellcode

    1. Imports System.Windows.Threading
    2. Class MainWindow
    3. Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
    4. Dispatcher.Invoke(DispatcherPriority.Normal, New Action(Of String)(AddressOf Test), "AHAAAAAA")
    5. End Sub
    6. Private Sub Test(text As String)
    7. Me.Title = text
    8. End Sub
    9. End Class
    Ich probier das mal aus, Anhand dessen

    VB.NET-Quellcode

    1. OnTrackEnd(syncHandle As Integer, channel As Integer, data As Integer, user As IntPtr)


    hab ich sofort gesehen Bass.dll, so schwer kann WPF ja nicht sein, als ich es das letzte mal versucht hab, hatte ich nicht mal DataBinding in WinForms drauf, schon Jahre her, aber das traue ich mir grad zu. Wollte immer noch mal einen Versuch mit WPF starten.
    Der Dispatcher bezieht sich doch auf ein Control? Wieso deklarierst du bitteschön dein eigenen Dispatcher?

    Einfach Application.Current.Dispatcher.Invoke, oder einfach Dispatcher.Invoke, der Methode ein delegate übergeben, die Priorität und dann anschließend die Methoden-Argumente spezifizieren.
    Siehe: docs.microsoft.com/en-us/dotne…cher-invoke(system-action)
    und
    docs.microsoft.com/en-us/dotne…r?view=windowsdesktop-6.0

    Geht das nicht?
    Und Gott alleine weiß alles am allerbesten und besser.
    @kafffee
    Mein Kopf funktioniert trotz einer Corona Infektion wieder :D , hatte erst ein Problem mit einer Exception:

    Assistent für verwaltetes Debuggen "CallbackOnCollectedDelegate" : "Für den von der Garbage Collection gesammelten Delegaten vom Typ "Bass.Net!Un4seen.Bass.SYNCPROC::Invoke" wurde ein Rückruf durchgeführt. Dies kann Anwendungsabstürze, Datenbeschädigung und -verlust zur Folge haben. Beim Übergeben von Delegaten an nicht verwalteten Code müssen die Delegaten von der verwalteten Anwendung beibehalten werden, bis sichergestellt ist, dass sie nie aufgerufen werden."


    Abgesehen vom SYNCPROC in der Klasse, dem Window_Loaded Event und dem Dispatcher, identisch mit WinForms ?(

    Als ich dann das SYNCPROC in der Klasse deklarierte anstatt in der Sub, war das auch gegessen. Sicher kriege ich noch einen auf'n Deckel von einem WPF Experten, aber das ist mein funktionierender Versuch.

    VB.NET-Quellcode

    1. Imports Un4seen.Bass
    2. Imports System.Windows.Threading
    3. Class MainWindow
    4. Private syncProc As SYNCPROC
    5. Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
    6. If Not Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero) Then
    7. MessageBox.Show("ERROR BASS INIT")
    8. Return
    9. End If
    10. Dim stream As Integer = Bass.BASS_StreamCreateFile("C:\Users\Eckat\Desktop\test.wav", 0, 0, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_AUTOFREE)
    11. If stream = 0 Then
    12. MessageBox.Show("ERROR BASS STREAM CREATE")
    13. Return
    14. End If
    15. syncProc = New SYNCPROC(AddressOf OnTrackEnd)
    16. Dim syncHandle As Integer = Bass.BASS_ChannelSetSync(stream, BASSSync.BASS_SYNC_END, 0, syncProc, IntPtr.Zero)
    17. If syncHandle = 0 Then
    18. MessageBox.Show("ERROR BASS SET SYNC")
    19. Return
    20. End If
    21. Bass.BASS_ChannelPlay(stream, False)
    22. End Sub
    23. Private Sub OnTrackEnd(syncHandle As Integer, channel As Integer, data As Integer, user As IntPtr)
    24. Dispatcher.Invoke(AddressOf TrackEnd)
    25. End Sub
    26. Private Sub TrackEnd()
    27. MessageBox.Show("TRACK ENDED!")
    28. End Sub
    29. End Class

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BitBrösel“ ()

    φConst schrieb:

    Der Dispatcher bezieht sich doch auf ein Control? Wieso deklarierst du bitteschön dein eigenen Dispatcher?


    Ne kein Control. @BitBrösel hat das sofort richtig erkannt, es geht um die bass.dll...

    Mein Code ist, bis auf die Benamungen, der Gleiche wie der von Bitbrösel. Bloss hab ich halt in meiner MVVM-Anwendung das erwähnte Problem mit dem Objektverweis... :(

    BitBrösel schrieb:

    so schwer kann WPF ja nicht sein


    Haha :P , das dachte ich auch am Anfang... Da wollte ich mein Projekt innerhalb zwei Wochen übersetzen... Ist jetzt ca. dreizehn Monate her... Ich dachte ich lern da bissle XAML und der Rest erledigt sich dann... Du musst differenzieren zwischen mit oder ohne MVVM-Pattern...
    Ich hatte erst das SYNCPROC in Zeile 19 deklariert und direkt eine Instanz erzeugt, also im Window_Loaded. Aufgrund der Meldung das
    die Delegaten von der verwalteten Anwendung beibehalten werden(anmerkung von mir: müssen), bis sichergestellt ist, dass sie nie aufgerufen werden."

    war mir klar, das durch deklarieren des SYNCPROC's im Gültigkeitsbereich der Klasse das SYNCPROC da bleibt.
    @BitBrösel

    Ich hab das exakt gleiche wie du, ausser den Benamungen.... Und natürlich ist das Ganze entkoppelt von der View, ich hab also keine Window_Loaded drin, sondern eine Methode, die gefeuert wird, wenn man den PlayButton drückt (nennt sich RelayCommand).

    Mein Projekt ist bissle gross für den Anfang, da würdest du dich nicht zurechtfinden.

    Aber gern mal ein kleines Beispiel, wo du sehr schön sehen kannst, wie das Binding von ViewModel und View aussehen könnte, die bass.dll hast ja bereits, die musst du in den Ausgabeordner von App und ViewModel kopieren... Und in Zeile 34 noch den Pfad einer MP3-Datei eingeben...

    Kannst auch gerne Fragen dazu stellen :)

    Im Grossen und ganzen kann man stark vereinfacht sagen: Du bindest die Eigenschaften deiner Controls aus der MainWindow.xaml an Klasseneigenschaften in der MainViewModel.vb
    Dateien
    Also ich sehe schon, reichlich Code für "wenig". Bin noch dabei die Architektur zu analysieren, sogar im Designer läuft die Musik, hatte mich gewundert wo die Musik herskommt, als ich den Designer öffnete. Könnte man da nun nicht einfach einen weiteren Service nutzen welcher eine Sub Invoke(action as Action) hat, und dort drin Application.Current.Dispatcher nutzen um die reinkommende Action auszuführen?

    Oder das einfach in WindowService reinmachen?
    @BitBrösel

    Echt bei dir läuft die Musik auch im Designer? Bei mir net.... Sowas kann natürlich Fluch und Segen sein, aus eigener Erfahrung. Du kannst das ganze auch unterbinden, in der Vorlage ist eine Eigenschaft IsInDesignMode implementiert, die kannst du benutzen um die unfreiwillige Ausführung von Codeteilen im DesignMode zu unterdrücken.
    Mit den Services weiss ich jetzt auch nicht, aber das hört sich bissle an wie von hinten durch die Brust ins Auge geschossen....

    Da muss es doch einen besseren Weg geben....

    kafffee schrieb:

    Ne kein Control. @BitBrösel hat das sofort richtig erkannt, es geht um die bass.dll...

    Du hast scheinbar nicht verstanden worauf ich hinaus will: Wieso deklarierst du ein Dispatcher-Objekt?? Die Dispatcher-Property wird zur Verfügung gestellt, das hat mit der bass.dll rein gar nichts zu tun.
    Jedes WPF-Objekt erbt von "DispatcherObject", demnach musst du den Dispatcher nicht selbst deklarieren/instanziieren. Eventuell solltest du dich erstmal einfach in WPF einarbeiten?
    Und Gott alleine weiß alles am allerbesten und besser.
    @kafffee Also nun läuft es nicht mehr im Designer, lag wohl daran das ich die DLLs gelöscht hab, Projekte neu erstellt, wodurch der Designer nicht funktionierte. Beim öffnen(bzw. neu laden) lief dann die Musik. Dachte schon ein Nachbar startet eine Party.

    Hab mir ein wenig das hier angeschaut und hab 'ne simple Lösung gefunden die in deinem Demo-Projekt funktioniert.
    docs.microsoft.com/de-de/archi…hing-in-mvvm-applications

    Einen Fehler hatte ich wenn das Lied zu Ende war, in der Sub UpdateVisualization, BMP war Nothing, check hinzugefügt. Auch ein SyncProc habe ich reingemacht, aber sieh selbst, In der Debug-Ausgabe wurde tatsächlich "Track Ended!" ausgegeben, ohne das mir was um die Ohren flog. Ist doch schon mal ein Anfang ^^ Da ja an der GUI nichts veränder wurde, dachte ich mir, wofür den Dispatcher?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Drawing
    3. Imports System.IO
    4. Imports System.Threading
    5. Imports System.Windows.Input
    6. Imports System.Windows.Media
    7. Imports System.Windows.Media.Imaging
    8. Imports System.Windows.Threading
    9. Imports Un4seen
    10. Imports Un4seen.Bass
    11. Public Class MainViewModel
    12. Inherits ViewModel.Instrastructure.ViewModelBase
    13. Public Spektrum As New Un4seen.Bass.Misc.Visuals
    14. Public streamfx As Integer
    15. Public VisualizationTimer As New DispatcherTimer
    16. Public BMP As Bitmap
    17. Public SyncProc As SYNCPROC
    18. Public Sub New()
    19. If IsInDesignMode = False Then
    20. cbxIndex = 0
    21. Aufloesung = 1
    22. Distanz = 0
    23. Debug.WriteLine("Gestartet")
    24. _Hoehe = 100
    25. _Breite = 350
    26. VisualizationTimer.Interval = TimeSpan.FromMilliseconds(25)
    27. AddHandler VisualizationTimer.Tick, AddressOf UpdateVisualization
    28. Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero, Nothing)
    29. streamfx = Bass.BASS_StreamCreateFile("dateiname.mp3", 0, 0, BASSFlag.BASS_STREAM_AUTOFREE Or BASSFlag.BASS_STREAM_PRESCAN)
    30. SyncProc = New SYNCPROC(AddressOf OnTrackEnd)
    31. Dim syncHandle As Integer = Bass.BASS_ChannelSetSync(streamfx, BASSSync.BASS_SYNC_END, 0, SyncProc, IntPtr.Zero)
    32. Bass.BASS_ChannelPlay(streamfx, False)
    33. VisualizationTimer.Start()
    34. End If
    35. End Sub
    36. Private Sub OnTrackEnd(syncHandle As Integer, channel As Integer, data As Integer, user As IntPtr)
    37. ThreadPool.QueueUserWorkItem(Sub(o)
    38. TrackEnd()
    39. End Sub)
    40. End Sub
    41. Private Sub TrackEnd()
    42. Debug.WriteLine("TRACK ENDED!")
    43. End Sub
    44. Private _cbxIndex As Integer
    45. Public Property cbxIndex As Integer
    46. Get
    47. Return _cbxIndex
    48. End Get
    49. Set(value As Integer)
    50. _cbxIndex = value
    51. RaisePropertyChanged()
    52. End Set
    53. End Property
    54. Private Sub UpdateVisualization(ByVal sender As Object, ByVal e As EventArgs)
    55. Select Case cbxIndex
    56. Case 0
    57. BMP = Spektrum.CreateSpectrumLine(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, 40, 5, False, True, True)
    58. Case 1
    59. BMP = Spektrum.CreateSpectrumBean(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, 40, False, True, True)
    60. Case 2
    61. BMP = Spektrum.CreateSpectrumDot(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, 40, 5, False, True, True)
    62. Case 3
    63. BMP = Spektrum.CreateSpectrumEllipse(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, 40, 5, False, True, True)
    64. Case 4
    65. BMP = Spektrum.CreateSpectrumLinePeak(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.SlateGray, System.Drawing.Color.Black, Aufloesung, 3, Distanz, 40, False, True, True) 'DAS HIER IST PERFEKT!!
    66. Case 5
    67. BMP = Spektrum.CreateSpectrum(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, False, True, True)
    68. Case 6
    69. BMP = Spektrum.CreateSpectrumText(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, "Hallo Welt", False, True, True)
    70. Case 7
    71. BMP = Spektrum.CreateSpectrumWave(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Black, Aufloesung, False, True, True)
    72. Case 8
    73. BMP = Spektrum.CreateWaveForm(streamfx, Breite, Hoehe, System.Drawing.Color.Green, System.Drawing.Color.IndianRed, System.Drawing.Color.Purple, System.Drawing.Color.Black, Aufloesung, True, False, True)
    74. End Select
    75. If BMP Is Nothing Then
    76. Return
    77. End If
    78. Bild = BitmapToImageSource(BMP)
    79. End Sub
    80. Private _StoppeVisualisierung As ICommand
    81. Public ReadOnly Property StoppeVisualisierung() As ICommand
    82. Get
    83. If _StoppeVisualisierung Is Nothing Then _StoppeVisualisierung = New Instrastructure.RelayCommand(AddressOf StoppeVisualisierung_Execute, Function(o) True)
    84. Return _StoppeVisualisierung
    85. End Get
    86. End Property
    87. Private Sub StoppeVisualisierung_Execute(obj As Object)
    88. VisualizationTimer.Stop()
    89. End Sub
    90. Private Function BitmapToImageSource(ByVal bitmap As Bitmap) As BitmapImage
    91. Using memory As MemoryStream = New MemoryStream()
    92. bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp)
    93. memory.Position = 0
    94. Dim bitmapimage As BitmapImage = New BitmapImage()
    95. bitmapimage.BeginInit()
    96. bitmapimage.StreamSource = memory
    97. bitmapimage.CacheOption = BitmapCacheOption.OnLoad
    98. bitmapimage.EndInit()
    99. Return bitmapimage
    100. End Using
    101. End Function
    102. Private _Bild As ImageSource
    103. Public Property Bild As ImageSource
    104. Get
    105. Return _Bild
    106. End Get
    107. Set(value As ImageSource)
    108. _Bild = value
    109. RaisePropertyChanged()
    110. End Set
    111. End Property
    112. Private _Breite As Integer
    113. Public Property Breite As Integer
    114. Get
    115. Return _Breite
    116. End Get
    117. Set(value As Integer)
    118. _Breite = value
    119. RaisePropertyChanged()
    120. End Set
    121. End Property
    122. Private _Hoehe As Integer
    123. Public Property Hoehe As Integer
    124. Get
    125. Return _Hoehe
    126. End Get
    127. Set(value As Integer)
    128. _Hoehe = value
    129. RaisePropertyChanged()
    130. End Set
    131. End Property
    132. Private _Aufloesung As Integer
    133. Public Property Aufloesung As Integer
    134. Get
    135. Return _Aufloesung
    136. End Get
    137. Set(value As Integer)
    138. _Aufloesung = value
    139. RaisePropertyChanged()
    140. End Set
    141. End Property
    142. Private _Distanz As Integer
    143. Public Property Distanz As Integer
    144. Get
    145. Return _Distanz
    146. End Get
    147. Set(value As Integer)
    148. _Distanz = value
    149. RaisePropertyChanged()
    150. End Set
    151. End Property
    152. End Class


    WPF mit MVVM wirkt schon recht kompliziert, hab ja so gut wie 0 Ahnung davon, aber mittlerweile fühle ich mich aktuell fähig das zu verstehen. Wenn ich da erstmal die Grundlagen(grundliegende Architektur) verstanden hab, wird es halb so schlimm sein, wie es grad aussieht. Ich denke ich schau mir in der nächsten Zeit mal das Tutorial vom Nofear23m an. Aber vorher kommen noch mal akustische Signale/NachrichtenTechnik(funkübertragung)/Modulation dran, such grad ein buch dafür, gab hier kürzlich ein Thema das mich interessiert, aber noch Wissen benötigt wird. Da war ich in der Lage ein nicht Kohärentes Signal zu erstellen(FSK, Frequency-Shift keying), aber bei einem Kohärenten gabs 'ne traurige Bruchlandung.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „BitBrösel“ ()

    @BitBrösel

    Jou mit dem ThreadPool hängt sich jetzt auch nichts mehr auf bei mir. Sehr Gut :thumbsup:

    FSK - Ist was aus dem Amateurfunkbereich oder?

    @φConst
    Ja wenn's um die Tiefen der WPF geht bin ich in der Tat noch etwas im Rückstand... Die Microsoft Docs sind leider streckenweise etwas schlecht übersetzt... Ich hatte jetzt nicht stundenlang recherchiert aber so nach einiger Zeit nix Interessantes gefunden. Bin eher so der Learning-By-Doing-Typ...

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