Nichtmodales Window wird nicht angezeigt

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

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von DTF.

    Nichtmodales Window wird nicht angezeigt

    Hey allerseits :)

    ich scheine irgendwie gesegnet zu sein mit schwer zu debuggenden Problemen. Hier das Szenario:


    VB.NET-Quellcode

    1. Dim windowService = ServiceContainer.GetService(Of IWindowService)
    2. Dim WartenVM As New ViewModel.WarteAufAktionViewModel
    3. WartenVM.Meldung = "Bitte warten..."
    4. MainModule.MainDispatcher.Invoke(Sub() windowService.OpenWindow(500, 150, "Liedtexte", WartenVM, Me, False, True, Services.WindowStyle.None, Services.ResizeMode.NoResize, Services.SizeToContent.Height, Services.WindowStartupLocation.CenterScreen, "Einen Moment Geduld...")) 'Aufruf des nictmodalen Windows
    5. Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow
    6. Dim OKVM = New OKDialogViewModel
    7. OKVM.Meldung = "Daten aus vorheriger Musikdatenbank werden gesichert. Dies kann einige Zeit in Anspruch nehmen..."
    8. MainModule.MainDispatcher.Invoke(Sub() dialogService.ShowModalDialog("", OKVM, Me, True, False, Services.WindowStyle.None, Services.ResizeMode.NoResize, 500, Services.SizeToContent.Height, Services.WindowStartupLocation.CenterScreen, "")) 'Aufruf der Messagebox
    9. Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait
    10. 'System.Threading.Thread.Sleep(7500)


    Ich möchte eigentlich bloss mit einem nichtmodalen Window (also so dass das Fenster angezeigt wird und das Programm aber weiterläuft im Code) dem User mitteilen, dass er ein bisschen Geduld haben muss...

    Oben schön zu sehen mein Workaround, also erst eine Art modale MessageBox, dann das nichtmodale Fenster mit dem Hinweis "Bitte warten...".

    Das ist das einzige was bis jetzt funktioniert hat.

    Wenn ich die Messagebox (OKVM) weglasse, dann erscheint das Window mit "Bitte warten..." gar nicht erst. Ich habs mit einem Haltepunkt versucht und auch mit Thread.Sleep, aber es scheint so, als würde die Zeile zum Window anzeigen gar nicht existieren.

    Also hab ich mal versucht, es vom Dispatcher des MainWindows aufzurufen, aber es bleibt gleich...

    Hat da jemand ne Ahnung was da im Busche sein könnte??
    Ich sehe es bei dem bisschen Code nicht.

    kafffee schrieb:

    aber es scheint so, als würde die Zeile zum Window anzeigen gar nicht existieren.

    Soll bedeuten, das der Haltepunkt nicht erreicht wird, oder wie darf ich das verstehen? Machst du viel im UI-Thread zu dem Zeitpunkt? Ich hatte es bei einem LoadingScreen(Window) das die UI nicht geupdated wurde, da musste ich dann sowas machen:(In der Klasse die das Interface für diesen Typ von Window implementiert)

    VB.NET-Quellcode

    1. public void UpdateProgress(ProgessReportInfoModel progress)
    2. {
    3. window.Dispatcher.Invoke(new Action(() =>
    4. {
    5. (window.DataContext as LoadingScreenViewModel).State = progress;
    6. }), DispatcherPriority.Render);
    7. }

    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    @DTF

    Eigentlich mach ich gar nix mit dem UI zu diesem Zeitpunkt. Da kommt direkt davor ein Modales Windows mit einer Fortschrittsanzeige, dann eine MessageBox , dass alles erledigt ist.
    Und dann erst dieser Code. Also das sollte eigentlich abgeschlossen sein. Ich hatte bereits das Problem dass diese Fortschrittsanzeige nicht geupdatet wurde, das liess sich aber beheben mit dem Invoke vom Dispatcher des MainWindow.
    Der Haltepunkt wird erreicht, aber das Window erscheint zu diesem Zeitpunkt einfach nicht. An anderer Stelle im Code funktioniert es tadellos, und der Code ist derselbe (copy und paste gemacht).

    Wie und von wo rufts du UpdateProgress auf? Vor dem Laden deines Loading Screens? Sollte ich als Argument dann WartenVM übergeben oder das aufrufende VM?

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

    kafffee schrieb:

    Wie und von wo rufts du UpdateProgress auf?

    C#-Quellcode

    1. AppServices.GetService<ILoadingScreenService>().Init(progress);//progress(ProgessReportInfoModel) hat auch strings da hab ich FensterTitel, InfoText usw. Die geb ich dann auch zum DataContext des Fensters siehe post vorher.
    2. solange laden dauert{
    3. //verarbeiten
    4. AppServices.GetService<ILoadingScreenService>().UpdateProgress(progress);
    5. }
    6. //...

    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Wie erstellst du denn die Fenster? Hast du die Instanzen nicht auch in einer Klasse, welche ein Interface speziell für das Window implementiert? Jedenfalls wird dort das Window erzeugt, und "progress" zugewiesen. ProgessReportModelInfo ist einfach eine Klasse, welche alle relevanten Daten hat, habe ich ja schon erwähnt. Oben im Code ist auch zu sehen, wie ich dem ViewModel den "progress" gebe, im View ist das natürlich gebunden.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    @DTF

    Okay hab mir das mal näher angeschaut, aber ich komm mit deinem (ersten) Code nicht klar.

    DTF schrieb:

    Wie erstellst du denn die Fenster? Hast du die Instanzen nicht auch in einer Klasse, welche ein Interface speziell für das Window implementiert?


    In meiner Klasse WindowService, die IWindowService implementiert, hab ich das hier:

    VB.NET-Quellcode

    1. Private GeoeffnetesFensterInstanz As View.DialogFenster
    2. Public Function OpenWindow(width As Integer, height As Integer, windowname As String, datacontext As Object, owner As Object, Optional topMost As Boolean = False, Optional showInTaskbar As Boolean = True, Optional WindowStil As ViewModel.Services.WindowStyle = ViewModel.Services.WindowStyle.ToolWindow, Optional Resize As ViewModel.Services.ResizeMode = ViewModel.Services.ResizeMode.CanResizeWithGrip, Optional AutoGroesse As ViewModel.Services.SizeToContent = ViewModel.Services.SizeToContent.Height, Optional StartPosition As ViewModel.Services.WindowStartupLocation = ViewModel.Services.WindowStartupLocation.CenterOwner, Optional Titel As String = "") As IntPtr Implements ViewModel.Services.IWindowService.OpenWindow
    3. Dim MeinFenster As New View.DialogFenster(windowname, datacontext, CType(AutoGroesse, Windows.SizeToContent), CType(StartPosition, Windows.WindowStartupLocation))
    4. _AktuellesFenster = MeinFenster
    5. MeinFenster.AsSpsModalDialog = False
    6. MeinFenster.Topmost = topMost
    7. MeinFenster.ShowInTaskbar = False
    8. MeinFenster.WindowStyle = CType(WindowStil, Windows.WindowStyle)
    9. MeinFenster.ResizeMode = CType(Resize, Windows.ResizeMode)
    10. MeinFenster.Owner = MeinFenster.FindOwnerWindow(owner)
    11. MeinFenster.SizeToContent = CType(AutoGroesse, Windows.SizeToContent)
    12. MeinFenster.WindowStartupLocation = CType(StartPosition, Windows.WindowStartupLocation)
    13. MeinFenster.Width = width
    14. MeinFenster.Title = Titel
    15. Application.Current.Dispatcher.Invoke(Sub() MeinFenster.Show())
    16. Dim FensterHandle As IntPtr = New WindowInteropHelper(MeinFenster).Handle
    17. GeoeffnetesFensterInstanz = MeinFenster
    18. Return FensterHandle
    19. End Function


    Dein Codeschnipsel sieht in VB bei mir so aus:

    VB.NET-Quellcode

    1. Public Sub UpdateProgress(progress As ViewModel.WarteAufAktionViewModel) Implements IWindowService.UpdateProgress
    2. GeoeffnetesFensterInstanz.Dispatcher.Invoke(New Action(Function()
    3. TryCast(GeoeffnetesFensterInstanz.DataContext, ViewModel.WarteAufAktionViewModel).State = progress
    4. End Function), DispatcherPriority.Render)
    5. End Sub


    Da kommt aber die Meldung "State ist kein Member von WarteAufAktionViewModel"

    Bei mir sieht der Aufruf so aus (also der Code, auf den gewartet werden soll, ist nicht in WarteAufAKtionViewModel, sondern in dem ViewModel, das es aufruft):

    VB.NET-Quellcode

    1. Dim windowService = ServiceContainer.GetService(Of IWindowService)
    2. Dim WartenVM As New ViewModel.WarteAufAktionViewModel
    3. WartenVM.Meldung = "Bitte warten..."
    4. MainModule.MainDispatcher.Invoke(Sub() windowService.OpenWindow(500, 150, "", WartenVM, Me, False, True, Services.WindowStyle.None, Services.ResizeMode.NoResize, Services.SizeToContent.Height, Services.WindowStartupLocation.CenterScreen, "Einen Moment Geduld..."))
    5. Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow
    6. Dim OKVM = New OKDialogViewModel 'wenn ich die Zeilen 6-8 weglasse, erscheint das Fenster nicht...
    7. OKVM.Meldung = "Daten aus vorheriger Musikdatenbank werden gesichert. Dies kann einige Zeit in Anspruch nehmen..."
    8. MainModule.MainDispatcher.Invoke(Sub() dialogService.ShowModalDialog("", OKVM, Me, True, False, Services.WindowStyle.None, Services.ResizeMode.NoResize, 500, Services.SizeToContent.Height, Services.WindowStartupLocation.CenterScreen, ""))
    9. Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait
    10. 'System.Threading.Thread.Sleep(7500)
    11. 'Hier kommt der Code, der ausgeführt werden soll und auf den gewartet werden soll
    12. windowService.CloseWindow() 'wenn ferig, dann soll hier das Fenster wieder geschlosssen werden.
    13. Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow


    Ich glaube, wir reden bisschen aneinander vorbei, wie gesagt ich verstehe deinen Code nicht ganz. Was mir Probleme macht, ist v.a. das New Action(....) über mehrere Zeilen, das versteh ich nicht.

    VB.NET-Quellcode

    1. ​Invoke(New Action(Function()
    2. TryCast(GeoeffnetesFensterInstanz.DataContext, ViewModel.WarteAufAktionViewModel).State = progress
    3. End Function), DispatcherPriority.Render)
    Okay danke dachte ich mir schon. Aber wie wende ich dann bei mir DispatcherPriority.Render an? In den MS Docs ist das nur so oberflächlich angerissen...

    Dein Szenario ist glaub doch ein bisschen anders... Weisst du wie ich mein?

    Edit: Aaaah ich glaub der Groschen ist gefallen:

    VB.NET-Quellcode

    1. MainModule.MainDispatcher.Invoke(Sub() windowService.OpenWindow(500, 150, "", WartenVM, Me, False, True, Services.WindowStyle.None, Services.ResizeMode.NoResize, Services.SizeToContent.Height, Services.WindowStartupLocation.CenterScreen, "Einen Moment Geduld..."), Threading.DispatcherPriority.Render)


    Aber das Fenster kommt trotzdem nicht.

    Sonst noch jemand ne Idee?

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

    Weiss nicht, kann aber sein das dein UI Thread beschäftigt ist oder das so schnell geht. Du hast ja auch mit Thread sleep propbiert, aber bringt ja nichts, wenn das fenster nicht angezeigt werden kann, weil der ui thread irgendwie blockiert ist. Ich glaub langsam du verennst dich, ich glaube du brauchst einen besseren Unterbau. Schau dir mal Dependency Injection an, kann leicht zu verstehen sein, hängt von dir ab. Würde aber auf einen rewrite hinauslaufen. Machst du das auch mit Plugins, hast du das alles noch abgekapselter.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Ich versuch jetzt einfach mal diese "Statusmeldung", dass der User warten soll, in einen vorhergehende Dialog mit einzubauen (da hab ich auch so nen ähnlichen Vorgang wie du), dachte zuerst das wär nicht ohne grösseren Umbau möglich, aber habs mir grad nochmal angeschaut, das müsste dann auf jeden Fall gehen.

    Rewrite mach ich jetzt deswegen keinen, bin schon seit 26 Monaten an dem Projekt und so langsam sollte ich mal ein Ende finden. Ist ja nur ein kleiner Schönheitsfehler. Sonst verrenn ich mich wirklich...

    Trotzdem wie immer danke für deine Mühen :)

    kafffee schrieb:

    bin schon seit 26 Monaten an dem Projekt und so langsam sollte ich mal ein Ende finden.


    Ein guter Programmierer ist nie fertig, es geht immer besser! Ich hab in den letzten 15 Jahren so viele Audioplayer gemacht. Immer kam was neues hinzu, oder es wurde was verbessert. Da hab ich sicherlich alle 2 Jahre neu angefangen. Jetzt allein mit WPF schon der 3 Anlauf(Jetzt hab ich WPF endlich ganz gut im Griff), wobei die ersten beiden Versionen schon faszinierend sind. Du hast ja den Screenshot gesehen von meinem Projektmappen-Explorer, das ist aber erst der Anfang, gerade mal der absolute Minimum. Der Aufwand den ich betreibe wird sich später auszahlen, wenn das alles fertig ist, liefer ich eine Art MediaBibliothekEngine aus, mit Basis-Plugins, damit Videos(entweder die LibVLC oder FFMpeg) und Audio abgespielt werden können. Dazu halt auch jede menge Interfaces, damit jeder sich selbst ein UI machen kann, oder eigene Sound-Effekt, Raab-Buttons(TV-Total),(Das geht doch nicht!) via Mausklick oder whatever, bishin zu Visualisierungen wie man sie vom WMP kennt.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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