Grafisches Problem - WinForms zu WPF

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

Es gibt 58 Antworten in diesem Thema. Der letzte Beitrag () ist von BitBrösel.

    Also ich hatte auch Probleme damit. Das Image-Control musste erst einmal gerendert werden, danach passte die Größe. Ob das beim Canvas auch zutrifft, musst du mal probieren.

    Gelöst habe ich das dann so:

    C#-Quellcode

    1. public BitmapSource SpektrumImageSource()
    2. {
    3. System.Windows.Size size = ServiceProvider.GetService<IMainWindowService>().GetSpektrumImageRenderSize();
    4. Bitmap bitmap;
    5. if (size.Width > 0 && size.Height > 0)
    6. {
    7. int w = (int)size.Width;
    8. bitmap = visuals.CreateSpectrumWave(Stream, (int)size.Width, (int)size.Height, Color.FromArgb(102, 102, 102), Color.FromArgb(30, 30, 30), Color.FromArgb(30, 30, 30), 2, true, true, true);
    9. }
    10. else
    11. {
    12. bitmap = visuals.CreateSpectrumWave(Stream, 10, 10, Color.FromArgb(102, 102, 102), Color.FromArgb(30, 30, 30), Color.FromArgb(30, 30, 30), 2, true, true, false);
    13. }
    14. if(bitmap == null)
    15. {
    16. return null;
    17. }
    18. return BitmapSourceFromImage(bitmap);
    19. }


    Im FensterService:

    C#-Quellcode

    1. public Size GetSpektrumImageRenderSize()
    2. {
    3. return Win.SpektrumImage.RenderSize;
    4. }
    Also wenn ich das richtig sehe prüfst du erstmal ob das Window schon gerendert ist, und wenn nicht, nimmst du erstmal feste Werte für Breite und Höhe.

    Hab auch mal probiert WFBreite und WFHoehe mit festen Werten zu verwenden, da kommt dann dieser Fehler (ohne Zeilenangabe):

    System.ArgumentException
    HResult=0x80070057
    Nachricht = "DependencySource" muss in demselben Thread wie "DependencyObject" erstellt werden.
    Quelle = WindowsBase
    Stapelüberwachung:
    at System.Windows.DependencyObject.ValidateSources(DependencyObject d, DependencySource[] newSources, Expression expr)
    at System.Windows.Expression.ChangeSources(DependencyObject d, DependencyProperty dp, DependencySource[] newSources)
    at System.Windows.Data.BindingExpressionBase.ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources)
    at System.Windows.Data.BindingExpressionBase.ChangeSources(WeakDependencySource[] newSources)
    at System.Windows.Data.BindingExpression.ChangeWorkerSources(WeakDependencySource[] newWorkerSources, Int32 n)
    at MS.Internal.Data.ClrBindingWorker.ReplaceDependencySources()
    at MS.Internal.Data.ClrBindingWorker.NewValueAvailable(Boolean dependencySourcesChanged, Boolean initialValue, Boolean isASubPropertyChange)
    at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
    at MS.Internal.Data.PropertyPathWorker.RefreshValue()
    at MS.Internal.Data.ClrBindingWorker.ScheduleTransferOperation(Object arg)
    at MS.Internal.Data.DataBindOperation.Invoke()
    at MS.Internal.Data.DataBindEngine.ProcessCrossThreadRequests()
    at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
    at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
    at System.Windows.Threading.DispatcherOperation.InvokeImpl()
    at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
    at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
    at System.Windows.Threading.DispatcherOperation.Invoke()
    at System.Windows.Threading.Dispatcher.ProcessQueue()
    at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
    at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
    at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
    at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
    at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
    at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
    at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
    at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
    at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
    at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
    at System.Windows.Application.RunDispatcher(Object ignore)
    at System.Windows.Application.RunInternal(Window window)
    at System.Windows.Application.Run(Window window)
    at System.Windows.Application.Run()
    at VamosALaPlayer_3._0.App.Application.Main()

    Diese Ausnahme wurde ursprünglich von dieser Aufrufliste ausgelöst:
    [Externer Code]
    Das hatte ich auch. Aber nach dem ich das mit der Waveform im BassLibService hatte, war das weg. Irgendwie bist du im falschen Thread, ich erzeuge MainWindowService und BassLibService im selben Thread vllt. geht es deshalb bei mir. Ein Blick in die Projektmappe und ich kann mehr dazu sagen.

    Hier zu sehen:
    Grafisches Problem - WinForms zu WPF

    Ich verwende ja ein MasterViewModel, darin manage ich die Kommunikation zwischen den ViewModels, über die IWindowServices komm ich an jeden DataContext(die ViewModels) und auch den Dispatcher, was nützlich weil manches der Bass in einem NebenThread läuft(z.B. das mit dem Callbacks)

    Edit @kafffee
    Kommst du an der Stelle an den Dispatcher? Versuch mal Dispatcher invoke, in einer anonymen Function dann Property = CreateBitmap

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

    BitBrösel schrieb:

    Kommst du an der Stelle an den Dispatcher? Versuch mal Dispatcher invoke, in einer anonymen Function dann Property = CreateBitmap


    Invoke kommt mir irgendwie bekannt vor, das war mal Thema, als ich das Ereignis handeln wollte, das mir anzeigt, dass ein Song zu Ende gespielt worden ist. Hat, egal wie ichs gemacht hab, nicht funktioniert. Irgendjemand von hier kam dann mit dem hier um die Ecke, und schwups, es hat funktioniert:

    VB.NET-Quellcode

    1. Private Sub WennTrackBeendet(syncHandle As Integer, channel As Integer, data As Integer, user As IntPtr)
    2. 'Dim MeinDispatcher As Dispatcher
    3. 'Dispatcher.CurrentDispatcher.Invoke(Sub() TrackBeendet(), New Object() {})
    4. ThreadPool.QueueUserWorkItem((Sub(o)
    5. TrackBeendet()
    6. End Sub))
    7. End Sub


    ...Habs grad mal an dieser Stelle mit ThreadPool.QueueUserWorkItem probiert, aber da kommt immer noch der gleiche Fehler.

    Was meinst du mit "anonymer Funktion"?
    Du meinst ich sollte es mal mit Dispatcher.CurrentDispatcher.Invoke(Sub() MeineFunktion(), New Object() {}) probieren, oder an was für eine Konstellation hast du da gedacht?

    Mich beschleicht ja so langsam der Gedanke, dass das gar nicht funktionieren kann, denn ich hab ja immer noch das Problem, dass in meinem Canvas hier...:

    XML-Quellcode

    1. <u:DataPiping.DataPipes>
    2. <u:DataPipeCollection>
    3. <u:DataPipe Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualWidth}"
    4. Target="{Binding Path=WFBreite, Mode=OneWayToSource}"/>
    5. <u:DataPipe Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualHeight}"
    6. Target="{Binding Path=WFHoehe, Mode=OneWayToSource}"/>
    7. </u:DataPipeCollection>
    8. </u:DataPiping.DataPipes>


    für die Bindings von WFBreite und WFHoehe keinen DataContext findet...

    Vielleicht sollte ich dafür mal noch ein separates Thema aufmachen.

    kafffee schrieb:

    ...Habs grad mal an dieser Stelle mit ThreadPool.QueueUserWorkItem probiert, aber da kommt immer noch der gleiche Fehler.

    Nachdem ich und @kafffee privat dran sitzen hier noch mal für alle, was das Problem mit den Threads und der DependencyProperty ist.

    Also Problem ist folgendes, Dispatcher.Currentdispatcher gibt dem Dispatcher für den aktuellen Thread zurück bzw. einen neuen wenn keiner für diesen Thread existiert, da die Funktion WennTrackBeendet von der Bass in einem Nebenthread gecallt wird, rappelt es auch wenn man dort einfach ThreadPool.QueueUserWorkItem verwendet, sind ja schliesslich im falschen Thread, bzw. ist der Dispatcher.Currentdispatcher der falsche für die GUI:

    Des Rätsels Lösung:
    In Window-Services eine Funktion rein welchen den Dispatcher des Windows rausgibt, diesen holen und mit diesem in den von der Bass im nebenthread gecallten Funktionen invoken.
    @BitBrösel

    Hey und einen schönen 1. Advent.

    Bin gerade dabei das Ganze abzuändern in meinem Projekt. Leider knallt es, wenn mein Programm versucht, auf Properties/Variablen aus meinem Service ZentraleKlasse zuzugreifen versucht.

    Das muss meines Erachtens daran liegen, dass ich ja jetzt in meinem WindowService das hier habe:

    Private Window As Window = New MainWindow

    Da wird eine weitere Instanz von MainWindow erzeugt, richtig?

    Ich dachte mir schon, dass genau das passieren würde....... Leider weiss ich auch keine Abhilfe. Weisst du vielleicht, wie ich an die richtige Instanz von MainWindow komme?
    Klar wird da eine Instanz vom MainWindow erstellt.

    Deshalb lege ich in der Application.xml auch keine StartUpUri fest, damit dort kein MainWindow erzeugt wird.
    Dafür nutze ich die Services, so erzeuge ich dann die Instanzen der Services, mit denen dann auch die Windows "bedient" werden können.

    VB.NET-Quellcode

    1. Class Application
    2. Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
    3. ServiceManager.GetInstance().AddService(Of IMainWindowService)(New MainWindowService())
    4. ServiceManager.GetService(Of IMainWindowService).Show()
    5. End Sub
    6. End Class


    An die Instanzen der WindowServices, kommst du dann so wie oben schon zu sehen., Wichtig ist damit der ServiceManager zwischen den Services unterscheiden kann, für jedes Fenster ein eigenes Interface zu haben. (IWindow2Service, IWindow3Service z.b.)

    VB.NET-Quellcode

    1. ServiceManager.GetService(Of IMainWindowService)


    So ist das einfacher mit den WindowInterfaces:

    VB.NET-Quellcode

    1. Public Interface IMainWindowService
    2. Inherits IWindowService
    3. End Interface

    VB.NET-Quellcode

    1. Public Interface IWindowService
    2. Sub Show()
    3. Sub Hide()
    4. Sub Close()
    5. Function GetDispatcher() As Dispatcher
    6. End Interface


    Dann jeweils klassen für jeden ServiceTyp anlegen, welche das jeweilige Interrface implementieren. Sollte in dem Demo Project das ich dir gab ersichtlich sein.

    PS.
    @kafffee Dir auch einen schönen 1. Advent.

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

    @BitBrösel

    Okay hab ich gemacht. Bei mir ist das alles anders benamt aber ich denke ich habs soweit richtig.

    Meine Application.xaml.vb sieht jetzt so aus:

    VB.NET-Quellcode

    1. Class Application
    2. Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
    3. ViewModel.Services.ServiceContainer.Instance.AddService(Of ViewModel.Services.IWindowService)(New Services.WindowService())
    4. ViewModel.Services.ServiceContainer.Instance.AddService(Of ViewModel.Services.IDialogWindowService)(New Services.DialogWindowService)
    5. ViewModel.Services.ServiceContainer.Instance.AddService(Of ViewModel.Services.IZentraleKlasse)(New Services.ZentraleKlasse)
    6. ViewModel.Services.ServiceContainer.Instance.AddService(Of ViewModel.Services.IMainWindowService)(New Services.MainWindowService)
    7. ViewModel.Services.ServiceContainer.GetService(Of ViewModel.Services.IMainWindowService).OpenWindow("Hauptfenster", New ViewModel.MainViewModel, Me)
    8. End Sub
    9. End Class


    Aber es kracht in Zeile 6:

    System.InvalidCastException
    HResult=0x80004002
    Nachricht = Das Objekt des Typs "VamosALaPlayer_3._0.App.Services.MainWindowService" kann nicht in Typ "VamosALaPlayer_3._0.ViewModel.Services.IMainWindowService" umgewandelt werden.

    Ich verstehe nicht wieso..... ;(
    ViewModel-Klasse != Service-Klasse eines Fensters.

    Du legst doch den DataContext(das ViewModel) im Window.DataContext fest oder nicht? Da wird dann deine ViewModel Instanz erzeugt.

    Jedes Fenster hat ein eigenes Interface welches vom gezeigten IWIndowService erbt.
    Für jedes FensterInterface eine Klasse anlegen welche das jeweilige Interface implementiert.

    Das/die ViewModel/s im DataContext der Fenster festlegen.

    Ich habe ein ApplicationViewModel, dort drin habe ich die Instanzen aller ViewModels, ich nutze nicht nur eins. Aber ich erzeuge dort keine ViewModels, ich hohle sie mir auch über die Services indem ich eine Funktion GetDataContext in den Fenster Servcies habe. Ich mach noch mal eine DemoMappe.

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

    So hab das eben gemacht, musste wieder feststellen das ich echt Probleme hab VB zu schreiben, hab mich so an C-Syntax gewöhnt.

    Analysiere nun die Architektur. Denke dabei nicht an dein Projekt, nur an dieses sonst wirst du wohl durcheinander kommen.

    Noch eine Anmerkung dazu:

    Um ViewModel oder WindowService übergreifende Kommunikation zu ermöglichen Events nutzen(das Thema hatten wir ja schon in den PNs)
    Entweder in den ViewModels Events einbauen die dann von außen abonniert werden können, oder in den Klassen für die FensterServices die Events einbauen, dann via ServiceManager den Service holen, um die Events aus den Services zu abonnieren.


    Die ApplicationViewModel könnte man sich sparen, aber ich nutze die gern. Falls du dich gleich fragen wirst warum die App sich beendet wenn Window1 geschlossen wird, findest du die Antwort in der Application.xml.

    Füge in der Mappe mal einen weiteren Service hinzu für die BASS also Interface + Klasse, wenn das auch ein Service ist, kannst du überall den Service holen und die BASS "steuern".

    Etwa so:

    VB.NET-Quellcode

    1. ​ServiceManager.GetService(Of IBasslibService).Stop()
    2. ServiceManager.GetService(Of IBasslibService).PlayUrl("url")
    3. dim vol as float = ServiceManager.GetService(Of IBasslibService).GetVolume()
    4. ServiceManager.GetService(Of IBasslibService).SetVolume(vol)
    Dateien
    • MVVM_Demo.zip

      (39,87 kB, 58 mal heruntergeladen, zuletzt: )

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

    @kafffee
    System.InvalidCastExceptionHResult=0x80004002Nachricht = Das Objekt des Typs "VamosALaPlayer_3._0.App.Services.MainWindowService" kann nicht in Typ "VamosALaPlayer_3._0.ViewModel.Services.IMainWindowService" umgewandelt werden.


    Nochmal dazu, hab mich iritieren lassen von deinen OpenWindow argumenten, jetzt fällt es mir grad auf. Sorry, ich hätte die fehlermeldung besser lesen sollen.

    VamosALaPlayer_3._0.App.Services.MainWindowService
    !=
    VamosALaPlayer_3._0.ViewModel.Services.IMainWindowService

    Klassen in verschiedenen Namespace können nie vom selben Typ sein. Aber in der DemoMappe, kannst du schauen in welchen Projekt ich die Interfaces hab, wie auch die Klassen welche die Interfaces implementieren. Kein Projekt in der Mappe hat einen Verweis auf das Applications-Projekt, aber andersrum sind alle bekannt. Daher haben da die Klassen und diese Interfaces für die Fenster nichts verloren.

    Bin mal wieder im Kopf ausgebrannt, ich mach erstmal 2 tage pause.
    @BitBrösel

    Ja hatte mir schon fast gedacht dass du da was verwechselt hast.
    Hatte das auch genau so gemacht wie du gesagt hattest.

    Was mich aber verwirrt ist, dass es ja in Zeile 3 meiner Applikation.xaml.vb genau so ist wie in Zeile 6 und da meckert er nicht...

    Egal, ich schau mal in die Mappe rein, da brauch ich denk ich sowieso zwei Tage dafür wie gesagt dass ich manchmal auch kein Kopf dafür habe, kenn ich von mir also gönn dir die Auszeit :-).

    Bis denne
    kaffee
    Naja gleich ist das wohl nicht, ich müsste da die Mappe sehen.
    Schick mir deine aktuelle Mappe per Email, dann schaue ich rein und kann mehr sagen. Jetzt buche ich erstmal ein Hotelzimmer und fahre dann zum 2-3 Tage pokern, zwischendurch kann ich dann in dein Projekt reinschauen. Lange nicht gepokert, dabei räumt sich mein Oberstübchen von allein wieder auf.

    Bis denn

    PS.
    Ist IMainWindowService ein Interface oder Klasse? Sollte Interface sein.
    Ist MainWindowService eine Klasse oder Interface? Sollte klasse sein, welche IMainWindowService implementiert.



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

    @BitBrösel

    Ja also IMainWindowService sollte ein Interface sein und MainWindowService eine Klasse, hab alles genauso gemacht wie von dir beschrieben (vorausgesetzt ich hab mich nicht irgendwo verklickt oder vertippt).

    Aber ich check das nochmal doppelt und schick dir bei Bedarf die Mappe, frühestens aber heute Abend bin heut schon komplett vollgeplant.

    Ich wünsch dir jedenfalls viel Spass beim Pokern und ein gutes Blatt :)
    Ja das kommt mir auch bekannt vor.

    Aber schön das du das denn doch noch gefunden hast. Sind aber nicht immer Typos, hatte kürzlich die Services in falscher Reihenfolge hinzugefügt und wunderte mich warum irgendwas "Null" war, hab auch sehr lange gebraucht diesen Fehler zu finden, überall gesucht, nur nicht an der richtigen Stelle.

    kafffee schrieb:

    Da hab ich schon nicht mehr dran geglaubt...


    In solchen Momenten denke ich mir immer:
    Die Hoffnung stirbt zuletzt, also nach mir. 8o Also immer dran bleiben!