Eigene Messagebox erstellen im MVVM-Pattern

  • WPF MVVM
  • .NET 5–6

Es gibt 86 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    So hat doch bissle länger gedauert, aber hier mal das Ergebnis der Discord Session mit @Nofear23m. Ich hab einen OK-Dialog, einen Ja/Nein-Dialog und einen minimalistischen FolderBrowser-Dialog implementiert, plus eine Statusanzeige, die nicht-modal angezeigt wird, also dass der Code weiterläuft, während das Fenster geöffnet ist...

    Der Messagetext und der Rückgabewert können einfach in einer Property des ViewModels hinterlegt werden.

    Eine Frage hab ich aber noch:

    Wie kann ich das MainWindow disablen, wenn ich ein nicht-modales Fenster anzeige? Also der Code soll nach dem Öffnen weiterlaufen aber das MainWindow wie gesagt disabled sein. Ich hab versucht die IsEnabled-Eigenschaft des MainWindow mit einer Property zu binden und dann einfach, während das Fenster offen ist, diese auf False zu setzen, da hat sich aber nix getan...
    Dateien

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

    kafffee schrieb:

    Ich hab versucht die IsEnabled-Eigenschaft des MainWindow mit einer Property zu binden und dann einfach, während das Fenster offen ist, diese auf False zu setzen, da hat sich aber nix getan...

    Da hat sich nichts getan ist keine qualifizierte aussage. Sorry.
    Bindingfehler? Haltepunkt gesetzt? XAML Livebaum überprüft?

    Setzen 5 :D
    Ne, im ernst. Gib korrekte Angaben.
    Aber wenn du damit nicht hinkommst, dann mach einen Border über das ganze Fenster und schalte diesen auf Visible, der liegt ja dann eh über allen anderen Controls, also kann man dann auch nichts mehr klicken. :huh:

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hab grad mal probiert das ohne Binding zu machen testweise. Wenn man IsEnabled = False macht kann der User ja trotzdem noch oben übers X das Fenster zumachen. So macht das für mich keinen Sinn.
    Muss ich dem User halt mitteilen, dass er keine Aktionen vornehmen soll stattdessen. Wäre schön gewesen wenn das wie beim DialogWindowService das Fenster komplett gesperrt hätte...

    kafffee schrieb:

    Wäre schön gewesen wenn das wie beim DialogWindowService das Fenster komplett gesperrt hätte...

    Das geht auch. du musst das schliessen ja nur abfangen und prüfen ob das "andere" Fenster noch offen ist und wenn nicht dann das schliessen verhindern. Ganz normales verhalten denn....

    Mit Enabled = False setzt du ja nur den Inhalt des Fensters. Das Fenster selbst muss ja trotzdem schliessbar sein. Wenn nicht geht das nur per Code und das musst du selbst implementieren.

    Aber genau für solche Szenarien gibts ja eben Dialogfenster. Da kommt der Benutzer nicht drum rum vorher den dialog zu schliessen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nofear23m schrieb:

    Aber genau für solche Szenarien gibts ja eben Dialogfenster. Da kommt der Benutzer nicht drum rum vorher den dialog zu schliessen


    Ich hab vor so eine Statusanzeige zu machen (es sollen die Id3-Tags der Mp3s eingelesen werden, was u. U. länger dauern kann) . Damit das funktioniert muss ich aber den Code weiterlaufen lassen nach dem Öffnen des Dialogs... Ist das irgendwie möglich indem man einen zweiten Thread laufen lässt oder sowas in der Art?
    Hallo
    Du denkst viel zu kompliziert.

    Mach dir ein ViewModel welches das einlesen macht und pack in dieses natürlich auch die Eigenschaften mit dem Fortschritt, also worauf du den Fortschrittsbalken bindest.
    Genau dieses ViewModel übergibst du als DataContext an ein Service welches einen Dialog öffnet. im aufrufenden VM bleibt der Code an der stelle stehen, das ist richtig, aber im VM für den Dialog läuft ja alles weiter.
    Dort kannst du also einlesen und den Fortschritt anzeigen. Alles gut.
    Und wenn das fertig ist Schliesst du das Fenster indem du dein DialogService holst und "CloseDialog()" aufrufst. Fertig.

    Da musst du dich nicht für verrenken.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    So, ich hab grad den ganzen Messagebox-Code in mein Projekt übertragen und bin nochmal auf ein Problem gestossen, wo ich mit meinem Latein am Ende bin:

    Ich rufe meinen selbst gebauten Ordnerbrowser folgendermassen auf:

    VB.NET-Quellcode

    1. Dim dialogService = ServiceContainer.GetService(Of IDialogWindowService)
    2. Dim OrdnerBrowserVM = New OrdnerBrowserViewModel
    3. dialogService.ShowModalDialog("", OrdnerBrowserVM, Me, True, True)
    4. Dim mp3Path As String = OrdnerBrowserVM.Ergebnis


    Der Ordnerbrowser öffnet und schliesst sich. Aber wenn ich einen Haltepunkt in die letzte Zeile mache, kommt der Code nie da an...

    Innerhalb des Ordnerbrowsers schliesse ich denselbigen mit:

    VB.NET-Quellcode

    1. Private _OK As ICommand
    2. Public ReadOnly Property OK() As ICommand
    3. Get
    4. If _OK Is Nothing Then _OK = New RelayCommand(AddressOf OK_Execute, Function(o) True)
    5. Return _OK
    6. End Get
    7. End Property
    8. Private Sub OK_Execute(obj As Object)
    9. Ergebnis = GewaehltesVerzeichnis
    10. Dim dialogService = ServiceContainer.GetService(Of IDialogWindowService)
    11. dialogService.CloseDialog()
    12. End Sub


    Der Ordnerbrowserdialog wird aber nicht von meinem MainViewModel aufgerufen, sondern von seinerseits einem anderen DialogFenster... und ich glaube das ist der Knackpunkt...
    Hallo

    Ich vermute den Fehler ja zu wissen (da ich dich mittlerweile ja kenne).
    Zeig mal das XAML des UserControls.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Die aufrufende View:

    XML-Quellcode

    1. ​<UserControl x:Class="StatusFensterView"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    6. xmlns:local="clr-namespace:VamosALaPlayer_3._0.View"
    7. xmlns:viewmodel="clr-namespace:VamosALaPlayer_3._0.ViewModel;assembly=VamosALaPlayer_3._0.ViewModel"
    8. mc:Ignorable="d"
    9. d:DesignHeight="450" d:DesignWidth="800">
    10. <Grid>
    11. <Grid.RowDefinitions>
    12. <RowDefinition Height="50*"/>
    13. <RowDefinition Height="50*"/>
    14. </Grid.RowDefinitions>
    15. <TextBlock Name="Statusmeldung" Grid.Row="0" Background="Transparent" Foreground="{DynamicResource VordergrundfarbeBrush}" Margin="30" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18" Text="{Binding Statusmeldung}"/>
    16. <ProgressBar Name="barFortschritt" Grid.Row="1" Height="35" Minimum="0" Background="Transparent" Foreground="{DynamicResource VordergrundfarbeBrush}" Margin="30" BorderBrush="{DynamicResource VordergrundfarbeBrush}" BorderThickness="1" Value="{Binding Fortschritt}" Maximum="{Binding MaximalerWert}"/>
    17. </Grid>
    18. </UserControl>


    Und der Ordnerbrowser:

    XML-Quellcode

    1. <UserControl x:Class="OrdnerBrowserView"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    6. xmlns:local="clr-namespace:VamosALaPlayer_3._0.View"
    7. xmlns:viewmodel="clr-namespace:VamosALaPlayer_3._0.ViewModel;assembly=VamosALaPlayer_3._0.ViewModel"
    8. mc:Ignorable="d"
    9. d:DesignHeight="450" d:DesignWidth="800">
    10. <Grid>
    11. <Grid.RowDefinitions>
    12. <RowDefinition Height="60*"/>
    13. <RowDefinition Height="20*"/>
    14. <RowDefinition Height="20*"/>
    15. </Grid.RowDefinitions>
    16. <ListBox Name="lstVerzeichnis" Grid.Row="0" Margin="20" ItemsSource="{Binding Verzeichnisse}" SelectionMode="Single" Background="Transparent" Foreground="Yellow" BorderBrush="Yellow" BorderThickness="1">
    17. <ListBox.ItemTemplate>
    18. <DataTemplate>
    19. <TextBlock Text="{Binding}" TextTrimming="CharacterEllipsis">
    20. <TextBlock.InputBindings>
    21. <MouseBinding MouseAction="LeftDoubleClick"
    22. Command="{Binding DataContext.OeffneVerzeichnis, ElementName=lstVerzeichnis}"
    23. CommandParameter="{Binding}"/>
    24. </TextBlock.InputBindings>
    25. <ToolTipService.ToolTip>
    26. <StackPanel>
    27. <TextBlock Text="{Binding}"/>
    28. </StackPanel>
    29. </ToolTipService.ToolTip>
    30. </TextBlock>
    31. </DataTemplate>
    32. </ListBox.ItemTemplate>
    33. </ListBox>
    34. <TextBlock Grid.Row="1" Text="{Binding GewaehltesVerzeichnis, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Margin="20" Background="Transparent" Foreground="Yellow"></TextBlock>
    35. <Grid Grid.Row="2">
    36. <Grid.ColumnDefinitions>
    37. <ColumnDefinition Width="50*"/>
    38. <ColumnDefinition Width="50*"/>
    39. </Grid.ColumnDefinitions>
    40. <Button Content="OK - Verzeichnis wählen" Margin="20" Grid.Column="1" Command="{Binding OK}"></Button>
    41. <Button Content="Zurück" Margin="20" Grid.Column="0" Command="{Binding OrdnerZurueck}"></Button>
    42. </Grid>
    43. </Grid>
    44. </UserControl>
    OK, das wars leider nicht.

    Bleibt nur noch der Setter des Property Ergebnis. Und zu Prüfen ob in "GewaeltesVerzeichnis" auch korrekt das Verzeichnis drinnen steht.
    Hast du CodeBehind im Usercontrol?

    Das Debuggen kann ich leider nicht für dich übernehmen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ja das Ergebnis wird korrekt übergeben. Im Testprojekt das ich dir hochgeladen hab funktionierts ja auch, da wird der Ordner dann über Debug.WriteLine ausgegeben. Der einzige Unterschied besteht darin, dass das wie gesagt nicht vom MainViewModel aufgerufen wird.
    Code Behind hab ich keinen da drinnen...

    Was auch komisch ist:

    Ich benutze in den Dialogen eine "{DynamicResource VordergrundfarbeBrush}", diese wird aber nicht verwendet, obwohl IntelliSense sie mir angezeigt hat und die Definitin in der Application.xaml steht... Ebenso ist es mit der StartUpLocation meines MainWindow: Ich öffne meine Dialoge ja mit CenterOwner. Das MainWindow öffnet sich auch maximiert, trotzdem werden meine Dialoge links oben angezeigt...

    Ich denke das hat vielleicht damit was zu tun...

    Mein StatusFensterViewModel wird von der Sub New() der SucheViewModel aus aufgerufen, also dem ViewModel eines UserControls (einer Registerkarte)...

    kafffee schrieb:

    Im Testprojekt das ich dir hochgeladen hab funktionierts ja auch

    Wo?
    Lade mal das aktuelle Projekt hoch. Ich glaube das du irgendwo eine neue Instanz erzeugst.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Leider sind da schon einige DLLs als Verweise zugefügt (hätte es ja über NuGet gemacht aber da waren nicht alle erhältlich die ich brauche).

    Aber ich lade dir das Projekt mal hier hoch, vielleicht kannst du dir trotzdem den Code anschauen ohne es auszuführen? Weiss nicht ob das so geht.

    Wenn du bestimmte Codestellen suchst und nicht findest helf ich dir gern.

    Ansonsten schick ich dir die DLLs gern als eMail...
    Dateien

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

    Hallo

    Ich weis nicht wie du Debuggst, aber bei mir funzt es wunderbar.
    Du hast ja noch eine Prozedur welche diesen Dialog aufruft innerhalb der Methode MusikbibliothekAktualisieren aber keine Ahnung was die aufgerufen wird.

    Aber in der von mir getesteten Methode funzt es.



    Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    @Nofear23m

    Na klar an dieser Stelle wollte ich eigentlich mein StatusFensterViewModel aufrufen. Dort wird ja dann in der MusikbibliothekAktualisieren der OrdnerBrowser aufgerufen. War also doppelt gemoppelt. Wie kann man nur so blind sein wie ich??

    Was gleich zum nächsten Problem führt:

    Im StatusFensterViewModel (nicht in der SucheViewModel(!)) rufe ich ja dann in Zeile 132 das auf:

    LayerViewModel.SucheViewModel.InhaltGesamt.Add(New MP3FileInfo(GeholteDateien(i), Tracknummer, Interpret, Album, Musiktitel, MeinUltraID3.Duration.ToString, MeinUltraID3.Year.ToString, False, Genre))
    und bekomme den Fehler: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. Und wenn ich über das LayerViewModel mit der Maus hovere, wird angezeigt, dass dieses Nothing ist.

    Kann ich mir nur so erklären, dass der Code von meiner Sub New() im SucheViewModel vor dem Code der Sub New() im MainViewModel ausgeführt wird... Kann das sein? Ich probier jetzt schon anderthalb Stunden daran rum und das ist mir die plausibelste Erklärung.
    Tja, das zeigt mir aber immer wieder das du nicht oder falsch Debuggst.
    Debuggen ist mitunter einer der wichtigsten Dinge im Leben eines Entwicklers.
    Und auch bei deinem jetzigen Fehler der übrigens nichts mehr mit dem Thread zu tun hat ist es so.

    Du vermutest etwas. Wie jetzt? Warum vermuten? Setze an beiden stellen einen Haltepunkt und schau welcher als erstes angelaufen wird. Gehe vom Konstruktor aus mit F10 bzw. F11 weiter und schau dir an wie sich dabei die Variablen verhalten.

    Dann kommst du schon drauf.

    Und lass das endlich mit diesem doppelt gemoppelt. Und hast soooo viele Dinge doppelt drinnen, leere Methoden, leer Ereignishandler usw.

    Ich wundere mich echt das du dich noch auskennst.
    Ich würde mich da als erstes jetzt mal nur mal einen ganzen Tag hinsetzen und alles Bereinigen, was nicht einfach ist. Man muss mal wissen was man noch braucht.

    Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nofear23m schrieb:

    Setze an beiden stellen einen Haltepunkt und schau welcher als erstes angelaufen wird. Gehe vom Konstruktor aus mit F10 bzw. F11 weiter und schau dir an wie sich dabei die Variablen verhalten.


    OK hab ich gemacht. Das wars schon mal nicht... Das liegt an der Instanziierung. Mein LayerViewModel ist nur für eine (!) Instanz gebaut würd ich sagen...

    Nofear23m schrieb:

    Und lass das endlich mit diesem doppelt gemoppelt. Und hast soooo viele Dinge doppelt drinnen, leere Methoden, leer Ereignishandler usw.


    Ist mir alles bewusst. Bissle kreatives Chaos ist es schon, da muss ich dir Recht geben :P Ich schaus mal durch...
    Naja, ich bin ja fast in versuchung zu sagen. Ich habs ja gesagt. Aber mach ich nicht. :)

    Im anderen Thread habe ich meine bedenken ja glaube ich geäussert. Nun bist du wohl an dem Punkt. :(

    Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    @Nofear23m

    Hey Sascha ich hoff dir gehts gut :)

    Ich hab mal noch ne Rückfrage, oder besser zwei, zum IDialogWindowService:

    (1) Wenn ich meinen Dialog wieder schliessen will, dann mach ich ja innerhalb des ViewModels des jeweiligen Dialogs:

    VB.NET-Quellcode

    1. Dim dialogService = ServiceContainer.GetService(Of IDialogWindowService)
    2. dialogService.CloseDialog()


    Woher weiss jetzt der dialogService, welcher Dialog jetzt gemeint ist? Weil das Öffnen des Dialogs findet ja im aufrufenden ViewModel statt:

    VB.NET-Quellcode

    1. Dim dialogService = ServiceContainer.GetService(Of IDialogWindowService)
    2. Dim OKVM = New OKDialogViewModel
    3. OKVM.Meldung = "Das ist die Meldung"
    4. dialogService.ShowModalDialog("", OKVM, Me, True, True)


    Benutzt dialogService da immer den zuletzt geöffneten Dialog, oder hängt das evtl. mit dem Owner, der als Argument übergeben wird, zusammen?

    Weil ich hab grad die Situation, dass von einem Dialog aus noch ein weiterer geöffnet wird, und da wird der zuerst geöffnete nicht geschlossen...?

    und (2)

    Meine Styles aus der Application.xaml scheinen für meine Dialoge keine Verwendung zu finden. Die müssten doch eigentlich applikationsweit Gültigkeit finden oder nicht?