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.

    @BitBrösel

    Na das ist doch schon mal was. OnRender und DrawingContext kannte ich jetzt noch nicht....

    Ich werd mich morgen auch mal an das Cropped Bitmap ranmachen, das klingt doch ganz vielversprechend.

    Das Bewegen und skalieren findest du ja schon in meinem Beispielprojekt, siehe die Variable Zoom und den Eventhandler vom Timertick... Da musst du wahrscheinlich aber noch die Einheiten in Pixel umrechnen damit das dann tadellos funktioniert... Weil Breite und Hoehe sind ja in Einheiten, und die Bass.dll will ja Pixels...

    Also: Mal gucken wer schneller ist :)

    PS: Zum testen empfehle ich dir eine Mp3 zu nehmen mit einem eindeutigen Intro und Zoom auf 10 oder so zu setzen, damit du genau überprüfen kannst "wo der Beat anfängt" bzw. ob es synchron läuft...

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

    Naja, das mit den Pixeln müsste ich bei meiner Variante nicht direkt. Deshalb ja %, 100 / TrackLength * TrackPosition, der Property MarkerPercentage geben und alles ist im Control geregelt. Brauche keine Width oder Height sonst wo, nur im OnRender des Controls. Klar hab ich so nicht die Größe des Controls um eine passgenaue Bitmap zu erstellen(im Fall von MVVM), das ließe sich aber auch über Umwege machen, aber muss glaub ich nicht sein, bisschen Skalieren wird keinem auffallen.(Zumindest nicht bei der WaveForm)

    PS.
    Ja das mit dem CroppedImage scheint bei mir nötig zu sein, denn DrawImage hat keine Überladung wo man ein Source-Rect und ein Destination-Rect angeben kann.(Wie es in Forms ist)

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

    @BitBrösel

    Also bei mir gehts tatsächlich so wie ich es schon habe. Einfach das Image auf dem Canvas verschieben. Hatte bloss im CreateBitmap -Call vergessen die Breite noch mit dem Zoomfaktor zu multiplizieren...

    Was mir grad mehr Sorgen bereitet ist das Finetuning. Denn wenn ich jetzt die ganzen Farben, Mono/Stereo, DrawBeat einstellen möchte, kommt bei mir nur noch ein schwarzes Bild raus, aber da guck ich mir jetzt gleich mal die Docs von radio42 an...

    VB.NET-Quellcode

    1. Private Sub InitialisiereWaveForm
    2. MeineWellenForm.ColorLeft = System.Drawing.Color.Aquamarine
    3. MeineWellenForm.ColorLeftEnvelope = System.Drawing.Color.Red
    4. MeineWellenForm.ColorMiddleLeft = System.Drawing.Color.Aquamarine
    5. MeineWellenForm.ColorRight = System.Drawing.Color.Aquamarine
    6. MeineWellenForm.DrawCenterLine = True
    7. MeineWellenForm.DrawEnvelope = False
    8. MeineWellenForm.DetectBeats = True
    9. MeineWellenForm.BeatLength = 1.0
    10. MeineWellenForm.BeatWidth = 1
    11. MeineWellenForm.DrawBeat = WaveForm.BEATDRAWTYPE.TopBottom
    12. MeineWellenForm.ColorBeat = System.Drawing.Color.Yellow
    13. MeineWellenForm.DrawMarker = WaveForm.MARKERDRAWTYPE.Line Or WaveForm.MARKERDRAWTYPE.Name Or WaveForm.MARKERDRAWTYPE.NamePositionMiddle
    14. MeineWellenForm.DrawWaveForm = WaveForm.WAVEFORMDRAWTYPE.Stereo
    15. End Sub


    Edit: OK also auf den ersten Blick auch nix gefunden...

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

    @kafffee

    Ich bin gerade an ganz anderer Stelle unterwegs. Ich hab die BassNet zerpflückt und selbst kompiliert, so das ich einen WaveForm-Konstruktor(mit Callback-Argument) ohne Forms.Control habe. In WinForms wird das Bild schon gezeigt bevor es ganz fertig ist(wird stück für Stück geupdated), daher finde ich das mit dem Callback so gut, in WPF müssen wir warten bis ganz gerendert wurde. Ich muss aber noch ein Problem lösen, wenn ich das zum Teil gerenderte Bild der Property zuweisen will knallt es.(Das Object wird bereits an andere Stelle verwendet), passiert auch wenn ich versuche die Bitmap zu klonen. Ich habe da schon einige Ideen, mal schauen wie ich versuche es anzugehen.

    Jetzt mach ich erst mal am CustomControl weiter, das gefällt mir besser als mit dem Canvas. So ist alles nötige im CustomContol, finde ich sauberer.

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

    Gibt da ein magisches Werkzeug für NET-Kompilate, nennt sich IlSpy :D Damit kannst du aus NET-DLLs/-EXEs wieder den Source herstellen(nicht 1:1), man kann das auch als VS-Projekt speichern, diese Projekte sind aber nicht immer ohne weiteres Lauffähig, hin und wieder muss man noch Hand anlegen, aber die aktuelle Version von IlSpy hat das gut mit der BassNet hinbekommen, war nicht mehr viel zu machen.

    github.com/icsharpcode/ILSpy

    Gibt es auch schon fertig kompiliert im MS-Store als Desktop-App.
    Hey @BitBrösel

    Und wie weit bist du gekommen mit deiner Callback-Funktion? Bei mir läuft es wie geschmiert bis auf den Fakt, dass wenn ich die grafischen Optionen festlegen will, mir aus irgendeinem Grund, die While-Schleife das gesamt Programm dauerhaft einfriert.... Hab das Problem auch mal im Forum von Un4Seen eingestellt, aber das ist jetz auch schon wieder ne Woche her...

    Sieht bei mir so aus:

    VB.NET-Quellcode

    1. Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero, Nothing)
    2. _stream = Bass.BASS_StreamCreateFile("C:\Test.mp3", 0, 0, BASSFlag.BASS_DEFAULT Or BASSFlag.BASS_STREAM_AUTOFREE Or BASSFlag.BASS_SAMPLE_FLOAT)
    3. Bass.BASS_ChannelPlay(_stream, False)
    4. MeinTimer.Interval = TimeSpan.FromMilliseconds(1000)
    5. AddHandler MeinTimer.Tick, AddressOf UpdateVisualization
    6. MeineWellenForm = New WaveForm("C:\Test.mp3")
    7. MeineWellenForm.ColorBackground = System.Drawing.Color.Red
    8. InitialisiereWaveForm()
    9. MeineWellenForm.RenderStart(True, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_PRESCAN Or BASSFlag.BASS_DEFAULT)
    10. Debug.WriteLine("Renderstart: " & CStr(Bass.BASS_ErrorGetCode))
    11. MeineWellenForm.SyncPlayback(_stream)
    12. Debug.WriteLine("Syncplayback: " & CStr(Bass.BASS_ErrorGetCode))
    13. While Not MeineWellenForm.IsRendered
    14. End While
    15. WellenForm = New Bitmap(MeineWellenForm.CreateBitmap(TransformToPixels(Breite) * Zoom, TransformToPixels(Hoehe), -1, -1, True))
    16. WaveFormSource = BitmapToImageSource(WellenForm)
    17. MeinTimer.Start()


    Und wenn ich dann das aufrufe:

    VB.NET-Quellcode

    1. Private Sub InitialisiereWaveForm()
    2. MeineWellenForm.ColorLeft = System.Drawing.Color.Aquamarine
    3. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    4. MeineWellenForm.ColorLeftEnvelope = System.Drawing.Color.Red
    5. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    6. MeineWellenForm.ColorMiddleLeft = System.Drawing.Color.Aquamarine
    7. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    8. MeineWellenForm.ColorRight = System.Drawing.Color.Aquamarine
    9. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    10. MeineWellenForm.DrawCenterLine = True
    11. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    12. MeineWellenForm.DrawEnvelope = False
    13. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    14. MeineWellenForm.DetectBeats = True
    15. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    16. MeineWellenForm.BeatLength = 1.0
    17. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    18. MeineWellenForm.BeatWidth = 1
    19. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    20. MeineWellenForm.DrawBeat = WaveForm.BEATDRAWTYPE.TopBottom
    21. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    22. MeineWellenForm.ColorBeat = System.Drawing.Color.Yellow
    23. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    24. MeineWellenForm.DrawMarker = WaveForm.MARKERDRAWTYPE.Line Or WaveForm.MARKERDRAWTYPE.Name Or WaveForm.MARKERDRAWTYPE.NamePositionMiddle
    25. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    26. MeineWellenForm.DrawWaveForm = WaveForm.WAVEFORMDRAWTYPE.Stereo
    27. Debug.WriteLine("Initialisierung: " & CStr(Bass.BASS_ErrorGetCode))
    28. End Sub


    Weisst du vielleicht, warum das ohne InitialisiereWaveform hinhaut, aber mit das ganze Fenster einfriert? Der ErrorCode gibt überall 0, also keine Fehler, zurück, auch bei SyncPlayback und RenderStart

    kafffee schrieb:

    Und wie weit bist du gekommen mit deiner Callback-Funktion?


    Also mit dem Callback da seh' ich mittlerweile schwarz. Ich konnte zwar ohne Probleme das Callback einbauen, aber der Zugriff auf das Bitmap geht in die Hose. Ich bekomme die Meldung das das Object bereits an anderer Stelle verwendet wird(selbst wenn ich das Bitmap Clone in der BassNet und dann ausgebe), ich müsste einiges in der BassNet ändern, das gefällt mir nicht, müsste ich immer wieder nach einem Update ändern, eben schnell die Überladung wär noch ok.

    Jetzt wo ich darüber schreibe glaube ich, ich sollte noch mal schauen ob das wegen etwas Threadübergreifendes ist und die Fehlermeldung mich auf eine falsche Fährte geführt hat. Wäre nicht das erste mal.

    Wenn es das nicht ist, werde ich selbst ein Bild rendern, das auslesen der Werte aus der Datei geht einfach, das malen wäre auch kein Problem, das hatte ich ja schon in WPF gemacht. So wäre dann auch das laden für's Auge besser, man sieht wie die Datei gescannt wird(alle x Frames die Property mit dem Image ändern), deutlich besser als wenn man erst nichts sieht und dann alles.

    PS. @kafffee
    Das Problem mit der Endlosschleife, weis nicht warum er nie Aufhört zu rendern, bzw. warum die Bass nicht fertig wird. Aber ich kann mir vorstellen, das es vorgesehen ist in dem Fall mit dem CallBack zu arbeiten, müsste man mal in Worms mit diesen Settings auf die Callback-Art machen, wenn da der Marker durch .DrawMarker ständig aktualisiert wird, wie auch beim Scannen der Datei(Audio), wird unter WinForms wenn man die Callback-Variante nimmt ds Bild immer aktualisiert, dann wird das der Grund sein warum du in der Endlosschleife festhängst.

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

    @kafffee
    Ein Verweis auf WInForms reicht, einfach Nothing für's Control reingeben. Wenn man diesen Konstruktor nimmt, muss nur System.Windows.Forms.Control bekannt sein. Also muss nur drauf verwiesen werden. Wenn der Kompiler nicht weiß wie viel platz(Bytes) er dort verwenden muss kann das nicht gehen, sobald drauf verwiesen ist, kennt er an dieser Stelle die Größe und kann das kompilieren.
    Ich habs, warum auch immer das jetzt geht.

    Der relevante Code, sieht genau so aus wie in WinForms, also das laden, man sieht wie das Bild mehrfach geupdated wird, wärend gescannt wird. Ich glaube fast weil ich das jetzt im Service machen, nicht im ViewModel selbst und das Event aus dem Service heraus feuer geht es.

    Wichtig! Das hier:

    C#-Quellcode

    1. WF = new WaveForm(lastSong, proc, IntPtr.Zero);

    Wird bei dir nicht gehen, Verweis auf WinForms und Nothing anstatt IntPtr.Zero, ich hab ja die editierte BassNet.

    C#-Quellcode

    1. public EventHandler<BitmapSource> WaveFormRendered;
    2. WaveForm WF = null;
    3. System.Drawing.Size WaveFormTargetSize;
    4. public void CreateWaveForm(int width, int height)
    5. {
    6. WAVEFORMPROC proc = new WAVEFORMPROC(WaveFormCallback);
    7. WF = new WaveForm(lastSong, proc, IntPtr.Zero);
    8. WF.FrameResolution = 0.01f;
    9. WF.CallbackFrequency = 2000;
    10. WF.DrawWaveForm = WaveForm.WAVEFORMDRAWTYPE.HalfMono;
    11. WaveFormTargetSize = new System.Drawing.Size(width, height);
    12. WF.RenderStart(true, BASSFlag.BASS_DEFAULT);
    13. }
    14. private void WaveFormCallback(int framesDone, int framesTotal, TimeSpan elapsedTime, bool finished)
    15. {
    16. if (WF != null)
    17. {
    18. BitmapSource bmps = BitmapSourceFromImage(WF.CreateBitmap(WaveFormTargetSize.Width, WaveFormTargetSize.Height, -1, -1, finished));
    19. WaveFormRendered?.Invoke(this, bmps);
    20. }
    21. }
    22. private BitmapSource BitmapSourceFromImage(Bitmap bmp)
    23. {
    24. if(bmp == null)
    25. {
    26. return null;
    27. }
    28. System.IO.MemoryStream memStream = new System.IO.MemoryStream();
    29. bmp.Save(memStream, System.Drawing.Imaging.ImageFormat.Png);
    30. PngBitmapDecoder decoder = new PngBitmapDecoder(memStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    31. return decoder.Frames[0];
    32. }
    @BitBrösel

    Ich versuche gerade deinen Code zu verstehen und mir sind folgende Fragen aufgekommen (hab mir alles nach VB übersetzen lassen mit so nem Onlinekonverter):

    (1) Public WaveFormRendered As EventHandler(Of BitmapSource)
    Ich kenne das jetzt nur so, z.B.:
    AddHandler BitmapSouce.Event, AddressOf WaveformRendered
    Wobei Event dann halt ein bestimmtes Event von BitmapSource ist, und WaveFormRendered eine Sub/Function die ausgeführt wird, wenn das Event auftritt...
    -->Also die Frage: Was ist das Event, und warum keine Sub/Function als EventHandler?

    (2) Das hier: WaveFormRendered?.Invoke(Me, bmps)
    Invoke soll ja denke ich heissen, dass was Bestimmtes aufgerufen wird. Aber was genau wird aufgerufen, wozu das Fragezeichen und was hat es mit den Argumenten Me und bmps auf sich?

    (3) Was genau macht BitmapSourceFromImage? Das Bitmap in PNG verwandeln? An welcher Stelle im Code lädtst du das dann in z.B. ein Image, um es anzuzeigen?

    Du siehst ich habe jede Menge Fragen... Ich hoffe das ist nicht zu viel auf einmal...
    Also da fehlen Grundlagen, also wie man Events deklariert, abonniert und feuert.

    kafffee schrieb:

    (1) Public WaveFormRendered As EventHandler(Of BitmapSource)
    Ich kenne das jetzt nur so, z.B.:
    AddHandler BitmapSouce.Event, AddressOf WaveformRendered


    Das erste ist eine Deklaration eines events, das andere ist zum abonnieren.

    Also diesen Code habe ich in einem Service, weil wir die Bass ja nur einmal laden brauchen aber auf keinen Fall zur Designtime, habe ich das BassZeugs in einem Service. Im ViewModel hole ich mir den, dort habe ich auch das Event abonniert,

    C#-Quellcode

    1. // some void
    2. {
    3. if (audioService.Init())
    4. {
    5. audioService.StreamFile(".mp3");
    6. audioService.WaveFormRendered += WaveFormRendered;
    7. audioService.CreateWaveForm(1200, 800);
    8. }
    9. else
    10. {
    11. Debug.WriteLine("Konnte BASS nicht laden!");
    12. }
    13. }
    14. private void WaveFormRendered(object sender, BitmapSource bitmapSource)
    15. {
    16. Bmp = bitmapSource;
    17. }
    18. private BitmapSource _bmp = null;
    19. public BitmapSource Bmp
    20. {
    21. get => _bmp;
    22. set
    23. {
    24. if(_bmp != value)
    25. {
    26. _bmp = value;
    27. RaisePropertyChanged(nameof(Bmp));
    28. }
    29. }
    30. }



    Zu 2: So feuert man Event in C#
    WaveFormRendered?.Invoke(Me, bmps)
    Die besonderheit des Fragezeichens ist ein "null-check". Ist das gleiche wie folgendes, aber ein Einzeiler.

    C#-Quellcode

    1. if(WaveFormRendered != null)
    2. {
    3. WaveFormRendered.Invoke(this, bmps);
    4. }


    Zu3, das habe ich so nur gemacht, weil ich noch ein Threading-Problem hatte, DependencyObject muss im selben Thread wie die Property erstellt werden. Das gab Probleme mit dem instanziieren eine BitmapSource. Ich mach dir mal eine kleine Mappe in VB fertig.

    PS. @kafffee
    Ich habe eine Projektmappe angehängt. Um das zum laufen zu bekommen, musst du im ViewModel-Projekt einen Verweis auf die BassNet machen, da die Mappe für x64 ausgelegt ist, die x64 bass.dll in Debug/Release-Ordner vom Program-Projekt kopieren, dann nur noch im ViewModel einen Pfad zu einer Audiodatei festlegen.
    Dateien

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

    @BitBrösel

    OK erst mal vielen Dank für deine Mühe, ist nicht selbstverständlich :)

    Ein bisschen klarer ist es jetzt...

    Ich weiss, bei mir fehlts teilweise an Grundlagen... bin auch jetzt noch net soo lang dabei...

    Bei der Verwendung von Events bin ich bis jetzt so klargekommen: (obwohl ich im Hinterkopf hab, dass es da noch andere Schreibweisen gibt...)

    Deklaration:
    Public <MeinEvent> As Event
    Abonnieren:
    AddHandler <Instanz>.<MeinEvent>, AddressOff <EventHandler>
    Feuern:
    RaiseEvent <MeinEvent>

    Bin gerade mal vier Stunden an der Sache drangesessen und hab das nicht hinbekommen, mit den Eigenschaften wie man den "Look" der WaveForm ändert...

    Dachte immer dass das daran liegt, dass er mit dem Rendern einfach net fertig wird aus irgendeinem Grund... Aber als das selbst bei deinem VB-Beispiel net geklappt hat, hab ich mich gewundert und hab nochmal ein komplett runter reduziertes WinForm-Testprogramm gemacht, das wirklich nichts anderes macht als die WaveForm zu zeichnen...

    Und hab dann jede Zeile einzeln getestet. Das Verändern der Farben hat funktioniert, bloss als es dann an das Anzeigen der Beat Lines ging, kam immer nur die Center Line zum Vorschein...

    Dann hab ich mich nochmal in den Un4seen/radio42-Docs schlau gemacht und siehe da, es lag daran, dass die bass_fx.dll im Ausgabeordner sein muss...

    Leider hat mich Bass.BASS_ErrorGetCode nicht darauf hingewiesen, was wünschenswert gewesen wäre, hab den Ian von Un4Seen aber schon drauf aufmerksam gemacht...

    Manchmal sucht man halt echt an der falschen Stelle... :/

    kafffee schrieb:

    Manchmal sucht man halt echt an der falschen Stelle...

    Oh ja, gestern hatte ich auch so einen Moment von Dauer.

    Mir flog immer eine Null-Reference-Exception um die Ohren, konnte mir das nicht erklären. Überall gesucht, nur nicht da wo es falsch war.
    Ändern mal in der App.Xaml.vb die Reihenfolge in welcher die Services hinzugefügt werden, so das der Fensterservice vorm BassLibService instanziiert wird(mach ich ja in der selben Zeile), heute morgen beim Kaffee da hatte ich dann den Geistesblitz. Wollte die Mappe eigentlich gestern schon anhängen.

    BitBrösel schrieb:

    Ändern mal in der App.Xaml.vb die Reihenfolge in welcher die Services hinzugefügt werden,


    Es kann so einfach sein 8o

    Das ist gut zum im Hinterkopf zu behalten, hab auch schon den ein oder andern Moment dran gedacht, dass mir das bei mir auch mal um die Ohren fliegen könnte...

    Zum Beispiel will ich, wenn der User das Programm ober rechts über X beendet, dass vorher noch etliche Einstellungen gespeichert werden, also sprich die Werte von Slidern, Textboxen, Checkboxen und so weiter... die sind teils in den ViewModels, aber auch in einem Service zu finden...
    Ich kann dir morgen via PN meine C# Projektmappe geben. Ich finde da hab ich die Architektur ganz gut. Solltest du dir mal anschauen, kurz erklärt:

    Über die Fensterservices komm ich an den DataContext der Fenster, jedes Fenster hat ein eigenes ViewModel. Dazu habe ich ein MasterViewModel, in diesem ViewModel habe ich Properties der anderen ViewModels, nun kommen die Services ins Spiel, über die hole ich den DataContext der Fenster, somit hab ich die ViewModel-Instanzen in der MasterViewModel klasse. Von dort aus wäre es einfach an all das zum speichern dran zu kommen. Im MasterViewModel ist dann auch der Service für die Bass zu finden, egal in welchem Fenster nun was geändert wird, alles ganz einfach zu managen.

    So schaut das dann in meiner App.Xml.cs aus:(Ich habe keine StartUpUri in der App.Xaml angegeben!)

    C#-Quellcode

    1. public MasterViewModel MasterViewModel = new MasterViewModel();
    2. protected override void OnStartup(StartupEventArgs e)
    3. {
    4. ServiceProvider.Instance.AddService<IBassNetService>(new BassNetService());
    5. ServiceProvider.Instance.AddService<IPickFileDialog>(new PickFileDialog());
    6. ServiceProvider.Instance.AddService<IPickFolderDialog>(new PickFolderDialog());
    7. ServiceProvider.Instance.AddService<IMainWindowService>(new MainWindowService());
    8. ServiceProvider.Instance.AddService<IEqualizerWindowService>(new EqualizerWindowService());
    9. ServiceProvider.GetService<IMainWindowService>().Show();
    10. MasterViewModel.MainWindowViewModel = ServiceProvider.GetService<IMainWindowService>().GetDataContext();
    11. MasterViewModel.EqualizerWindowViewModel = ServiceProvider.GetService<IEqualizerWindowService>().GetDataContext();
    12. base.OnStartup(e);
    13. }
    14. protected override void OnExit(ExitEventArgs e)
    15. {
    16. base.OnExit(e);
    17. ServiceProvider.GetService<IBassNetService>()?.Shutdown();
    18. }

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

    @BitBrösel

    ich hab gerade das Testprogramm ins Hauptprojekt integriert und hab hier nochmal einen Fehler der mich in den Wahnsinn treibt:

    VB.NET-Quellcode

    1. Private MeineWellenForm As New WaveForm()
    2. Private WellenForm As Bitmap
    3. [...]
    4. WellenForm = New Bitmap(MeineWellenForm.CreateBitmap(TransformToPixels(WFBreite) * Zoom, TransformToPixels(WFHoehe), -1, -1, True))


    Hier bekomm ich die Fehlermeldung:

    System.NullReferenceException
    HResult=0x80004003
    Nachricht = Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    Quelle = System.Drawing
    Stapelüberwachung:
    at System.Drawing.Bitmap..ctor(Image original)
    at VamosALaPlayer_3._0.ViewModel.DeckViewModel.Abspielen_Execute(Object obj) in F:\Programme\VS2022\VALP 3.0 4.8 Stand 29.08.2022\VamosALaPlayer 3.0\VamosALaPlayer 3.0\VamosALaPlayer_3._0.ViewModel\DeckViewModel.vb:line 154

    Versteh ich nicht, ich hab doch MeineWellenForm instanziiert. Hast du ne Ahnung?
    Ich glaub das schaffen wir auch so :)

    Der Fehler liegt im XAML denke ich.

    Da bleiben ja nur noch WFBreite und WFHoehe. Und in der Tat, Haltepunkt gesetzt, und sie sind beide 0, anders als im Testprogramm, da haben sie einen Wert.

    Mein XAML sieht so aus:

    XML-Quellcode

    1. xmlns:vm="clr-namespace:VamosALaPlayer_3._0.ViewModel.DataGridMovableRows;assembly=VamosALaPlayer_3._0.ViewModel"
    2. xmlns:u="clr-namespace:VamosALaPlayer_3._0.ViewModel.u;assembly=VamosALaPlayer_3._0.ViewModel"
    3. xmlns:mouse="clr-namespace:VamosALaPlayer_3._0.ViewModel.Maus;assembly=VamosALaPlayer_3._0.ViewModel"
    4. [...]
    5. <Canvas Grid.Row="0">
    6. <Canvas.InputBindings>
    7. <MouseBinding Command="{Binding MausRadUp}">
    8. <MouseBinding.Gesture>
    9. <mouse:MouseWheelUp />
    10. </MouseBinding.Gesture>
    11. </MouseBinding>
    12. <MouseBinding Command="{Binding MausRadDown}">
    13. <MouseBinding.Gesture>
    14. <mouse:MouseWheelDown />
    15. </MouseBinding.Gesture>
    16. </MouseBinding>
    17. </Canvas.InputBindings>
    18. <u:DataPiping.DataPipes>
    19. <u:DataPipeCollection>
    20. <u:DataPipe Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualWidth}"
    21. Target="{Binding Path=WFBreite, Mode=OneWayToSource}"/>
    22. <u:DataPipe Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualHeight}"
    23. Target="{Binding Path=WFHoehe, Mode=OneWayToSource}"/>
    24. </u:DataPipeCollection>
    25. </u:DataPiping.DataPipes>
    26. <Image Source="{Binding WFBild, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" Stretch="Fill" Canvas.Top="0" Canvas.Left="{Binding XPosition, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
    27. <Line Stroke="{DynamicResource VordergrundfarbeBrush}" StrokeThickness="1" Y1="0" Y2="100" X1="{Binding X_1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" X2="{Binding X_1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
    28. </Canvas>


    Da streicht er mir in Zeile 21 und 23 WFBreite bzw. WFHoehe an mit der Meldung "Für die Bindung WFBreite bzw. WFHoehe wurde kein DataContext gefunden."

    Per IntelliSense zeigt er mir dann ein paar meiner ViewModels an, aber wenn ich DeckViewModel auswähle, scheint sich im Code nichts zu ändern und auch funktionieren will es nicht. Der Clou ist, dass das entsprechende VM per Code geladen wird:

    Public Property DeckLeft As ViewModel.DeckViewModel = New ViewModel.DeckViewModel(1)

    Meine Klasse DataPiping sieht so aus:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Windows
    2. Namespace u
    3. Public Class DataPiping
    4. Public Shared ReadOnly DataPipesProperty As DependencyProperty = DependencyProperty.RegisterAttached("DataPipes", GetType(DataPipeCollection), GetType(DataPiping), New Windows.UIPropertyMetadata(Nothing))
    5. Public Shared Sub SetDataPipes(ByVal o As DependencyObject, ByVal value As DataPipeCollection)
    6. o.SetValue(DataPipesProperty, value)
    7. End Sub
    8. Public Shared Function GetDataPipes(ByVal o As DependencyObject) As DataPipeCollection
    9. Return CType(o.GetValue(DataPipesProperty), DataPipeCollection)
    10. End Function
    11. End Class
    12. Public Class DataPipeCollection
    13. Inherits FreezableCollection(Of DataPipe)
    14. End Class
    15. Public Class DataPipe
    16. Inherits Freezable
    17. Public Property Source As Object
    18. Get
    19. Return CObj(GetValue(SourceProperty))
    20. End Get
    21. Set(ByVal value As Object)
    22. SetValue(SourceProperty, value)
    23. End Set
    24. End Property
    25. Public Shared ReadOnly SourceProperty As DependencyProperty = DependencyProperty.Register("Source", GetType(Object), GetType(DataPipe), New FrameworkPropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf OnSourceChanged)))
    26. Private Shared Sub OnSourceChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    27. CType(d, DataPipe).OnSourceChanged(e)
    28. End Sub
    29. Protected Overridable Sub OnSourceChanged(ByVal e As DependencyPropertyChangedEventArgs)
    30. Target = e.NewValue
    31. End Sub
    32. Public Property Target As Object
    33. Get
    34. Return CObj(GetValue(TargetProperty))
    35. End Get
    36. Set(ByVal value As Object)
    37. SetValue(TargetProperty, value)
    38. End Set
    39. End Property
    40. Public Shared ReadOnly TargetProperty As DependencyProperty = DependencyProperty.Register("Target", GetType(Object), GetType(DataPipe), New FrameworkPropertyMetadata(Nothing))
    41. Protected Overrides Function CreateInstanceCore() As Freezable
    42. Return New DataPipe()
    43. End Function
    44. End Class
    45. End Namespace


    Und das sind die Properties:

    VB.NET-Quellcode

    1. ​Private _WFBreite As Double
    2. Public Property WFBreite As Double
    3. Get
    4. Return _WFBreite
    5. End Get
    6. Set(value As Double)
    7. _WFBreite = value
    8. RaisePropertyChanged()
    9. End Set
    10. End Property
    11. Private _WFHoehe As Double
    12. Public Property WFHoehe As Double
    13. Get
    14. Return _WFHoehe
    15. End Get
    16. Set(value As Double)
    17. _WFHoehe = value
    18. RaisePropertyChanged()
    19. End Set
    20. End Property

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