Eigene Messagebox erstellen im MVVM-Pattern

  • WPF MVVM
  • .NET 5–6

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

    Eigene Messagebox erstellen im MVVM-Pattern

    Hallo miteinander :)

    ich bin gerade dabei mir eine eigene MessageBox für meine MVVM-Anwendung zu basteln und habe dazu noch einige Fragen (das Fenster selber ist im XAML natürlich schon gebastelt...). Da gibt es schon sehr viel im Internet, hab aber nix speziell für MVVM gefunden und wenn ja, dann wars zu kompliziert für mich...

    Und zwar wie mach ich es am blödesten?:

    (1) Die MessageBox überhaupt anzuzeigen? Name geben, instanziieren und dann .Show? Das ist glaube ich nicht ganz so im Sinne des MVVM...

    (2) Beim Anzeigen des MessageBox-Fensters das eigentliche Anwendungsfenster zu disablen? Einfach die IsEnabled-Eigenschaft des Anwendungsfensters binden und dann auf False setzen?

    (3) Bei einem Fenster gibt es ja die Eigenschaft SizeToContent. Gibt es sowas ähnliches auch für einen Textblock, so dass die Höhe des Textblocks automatisch an die Länge des Textes angepasst wird? Oder muss ich wohl oder übel auf ein Panel mit Scrollbar zurückgreifen?

    (4) Sollte ich das Anwendungsfenster als Parent oder wo etwas festlegen? Und wenn ja, geht das vom XAML aus?

    (5) Wie kann ich den Code anhalten während die Messagebox geöffnet ist?

    (6) Gibt es sonst noch was zu beachten and das ich jetzt noch nicht gedacht habe?

    Ich bin gespannt auf eure Antworten.

    Bis dahin,

    kafffee

    Edit: Hab das hier gefunden was ich halbwegs verstehe, aber da geht es glaub ich wieder um vorgefertigte Dialoge und nicht "custom-made" wenn ich das richtig verstanden habe...:

    codeproject.com/Articles/36745…-WPF#wpf_usage_showdialog

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

    Ich bin leider weit von guten Ratschlägen entfernt, aber ein ähnliches Thema hatte ich auch schon gestartet, bei dem @Nofear23m was dazu geschrieben und gecodet hat, wie man andere Fenster MVVM-konform (?) aufruft.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hallo

    Leider bin ich gerade nur sporadisch Online da ich am Übersiedeln in des 300km entfernte Wels bin. Sitze gerade mit dem Laptop hier, umzingelt von unzähligen Umzugskisten ;)

    Zu 1.) Richtig, das wäre alles andere als MVVM Konform. Deshalb erstellt man einen Servicecontainer und Abstrahiert UI Elemente mittels Interfaces.
    @VaporiZed hat dir eh einen Beitrag verlinkt wie ich das immer mache. Ansonsten haste auch ein Beispiel in meinem WPF Notes Projekt hier im Forum.

    Zu 2.) Wenn du es unbedingt Disablen willst gerne, musste aber garnicht. Nur falls du dies mit einem Halbtransparent-Schwarzem Panel anzeigen willst.

    Zu 3.) Ich weis es jetzt nicht genau obs das LAbel oder der TextBlock war (ich glaube das LAbel) welches eine Eigenschaft TextWraping hat. Probier diese mal.

    Zu 4.) Wenn du eine eigene Messagebox machen willst dann musst du entscheiden ob und wann du einen Owner übergeben willst. Je nachdem wie Messagebox dann auch in der Taskleiste angezeigt werden soll.

    Zu 5.) Anhalten? Wenn du ein Fenster mit .ShowDialog öffnest haltet der Code an dieser stelle sowieso. Oder meinst was anderes?

    Zu 6.) Einige Kleinigkeiten gibt es sicher noch, aber probiers einfach, da kommste recht schnell dahinter. Und wenn du es mal hast ist das schöne das du dieses "Service" immer und überall verwenden kannst.

    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. ##

    @VaporiZed

    Super danke für den Link sowas in der Art hatte ich gesucht.

    @Nofear23m

    Wow sehr löblich dass du trotzdem hier online gehst, ich weiss noch wie das bei meinem letzten Umzug war, da hatte ich quasi noch nicht mal ne funktionierende Küche und gerade mal zwei Tage Zeit alls umzuziehen, aufzubauen und wieder auszupacken bevor dann der neue Job angefangen hat...

    Ich denke mit dem Link kann ich was anfangen, ich probier mal das nachzubauen und wenn Fragen sind meld ich mich nochmal...

    Bis dahin viel Glück in der neuen Umgebung :)
    Aus Erfahrung kann ich sagen so ein Neuanfang bietet auch immer eine neue Chance...


    kafffee
    Hallo

    Ich bin dann langsam wieder Einsatzbereit.
    Wie weit bist du gekommen? Sind noch Fragen aufgetaucht? Ich nehme es schon an aber wer weis. ;)

    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

    Erstmal gutes Neues :) !

    Bin soweit ganz gut durch gekommen, bis auf eins:

    VB.NET-Quellcode

    1. ​Namespace Services
    2. Public Class DialogWindowService
    3. Implements ViewModel.Services.IDialogWindowService
    4. Private _currentSpsWindow As View.DialogFenster
    5. Public Function ShowModalDialog(windowname As String, datacontext As Object, owner As Object, Optional topMost As Boolean = False, Optional showInTaskbar As Boolean = True) As Boolean Implements ViewModel.Services.IDialogWindowService.ShowModalDialog
    6. Dim spswin As New View.DialogFenster(windowname, datacontext, SizeToContent.WidthAndHeight, WindowStartupLocation.CenterOwner)
    7. _currentSpsWindow = spswin
    8. spswin.Topmost = topMost
    9. spswin.ShowInTaskbar = showInTaskbar
    10. spswin.Owner = spswin.FindOwnerWindow(owner)
    11. Return CType(Application.Current.Dispatcher.Invoke(Function() spswin.ShowDialog), Boolean)
    12. End Function
    13. Public Sub CloseDialog() Implements ViewModel.Services.IDialogWindowService.CloseDialog
    14. If _currentSpsWindow IsNot Nothing Then
    15. _currentSpsWindow.Close()
    16. End If
    17. End Sub
    18. End Class
    19. End Namespace


    In Zeile 12 zeigt er mir an:
    "FindOwnerWindow" ist kein Member von "DialogFenster""

    Da komm ich nicht mehr mit...
    Das ist ja in der CodeBehind-Datei der DialogWindow.XAML zu finden:

    VB.NET-Quellcode

    1. Public Shared Function FindOwnerWindow(viewModel As Object) As Window
    2. ' if the ViewModel of the caller is set, we try to find the corresponding Window
    3. If viewModel IsNot Nothing Then
    4. If viewModel.GetType Is GetType(DialogWindow) Then Return CType(viewModel, Window)
    5. If Application.Current.Windows.Cast(Of Window)().SingleOrDefault(Function(x) x.DataContext IsNot Nothing AndAlso x.DataContext.GetType Is viewModel.GetType) Is Nothing Then
    6. For Each window As Window In (From win In Application.Current.Windows()).ToList
    7. If window.DataContext IsNot Nothing AndAlso window.DataContext.GetType Is viewModel.GetType Then
    8. Return window
    9. End If
    10. Next
    11. Else
    12. Return Application.Current.Windows.Cast(Of Window)().SingleOrDefault(Function(x) x.DataContext IsNot Nothing AndAlso x.DataContext.GetType Is viewModel.GetType)
    13. End If
    14. End If
    15. ' by default we use the active Window in the List as owner
    16. Return Application.Current.Windows.Cast(Of Window)().SingleOrDefault(Function(x) x.IsActive)
    17. End Function

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Genau

    Hier wird aus allen offenen Fenstern das Fenster ermittelt welchen als DatenKontext den Typ des übergebenen ViewModels entspicht.
    Im Grunde könnte man diese Prozedur auch in die Serviceklasse packen, sie muss nicht in der CodeBehind sein.

    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. ##

    Bin gerade dabei es mal zu testen.

    Mein Button auf dem MainWindow wird zur Designzeit angezeigt, zur Laufzeit aber nicht, da gibts nur ein weisses Fenster. Woran kann denn das liegen, ich weiss ehrlich gesagt nicht wo ich da zu suchen anfangen soll...

    Im Anhang mal der Text der Ausgabe.
    Dateien
    • Ausgabe.txt

      (7 kB, 132 mal heruntergeladen, zuletzt: )
    Aber Kaffee....

    Mit der Ausgabe fange ich leider nix an.

    Was ist Mainwindow? Das Window welches du von deinem Service aus aufrufst?
    Zur Designzeit? Ist das ein DataTemplate? Sollte es ja (oder besser muss) und wie schaut dieses aus?

    Poste die Serviceimplementierung und den XAML sowie den CodeBehind des Fensters welches du vom Service aufrufst. Und dann noch das DataTemplate mit welchem dein ViewModel welches du als DataContext übergibst verknüpft ist.

    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. ##

    kurze Zwischenfrage zu FindOwnerWindow, weil ich selber mal wieder dabei bin, die ganzen Geschichten nachzuvollziehen. Diesen Teil versteh ich (wohl) noch nicht:

    VB.NET-Quellcode

    1. If Application.Current.Windows.Cast(Of Window)().SingleOrDefault(Function(x) x.DataContext IsNot Nothing AndAlso x.DataContext.GetType Is viewModel.GetType) Is Nothing Then
    2. For Each window As Window In (From win In Application.Current.Windows()).ToList
    3. If window.DataContext IsNot Nothing AndAlso window.DataContext.GetType Is viewModel.GetType Then
    4. Return window
    5. End If
    6. Next

    Unter welchen Bedingungen trifft dieser Teil zu? Wenn über den LINQ-Ausdruck nix gefunden wird, wie(so) kann dann über die effektiv selbe Geschichte per For-Schleife was gefunden werden?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ne ganz einfach: Das Ganze ist nur ein Testprojekt und da hab ich in meinem MainWindow einfach ein Button reingeklatscht, und wenn man auf den klickt, soll das Dialogfenster angezeigt werden. Hat also gar nichts mit der eigentlichen "Aufgabenstellung" zu tun...

    Der Einfachheit halber im Anhang mal das Testprojekt.
    Dateien
    Bin jetzt verwirrt, dass das MainWindow in der View-Assembly ein Window und kein UserControl ist, was dann im MainWindow (einem Window) in der App-Assembly angezeigt wird. Denn so kann es ja nicht in der App-Assembly per XAML eingebunden werden. Aber vielleicht blick ich da so einiges noch nicht …
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Unter welchen Bedingungen trifft dieser Teil zu?

    Da gebe ich dir völlig recht und ist (fast) redundant. Ich weis gar nicht warum ich das so gemacht habe. Entweder das wurde vergessen zu löschen nach einen refactoring (von Schleife auf Linq) oder ist hatte es reingemacht weil es mal ein Problem mit dem Cast zu Window gab. müsste ich mal Testen.
    Aber seeeehhhrr gut aufgepasst. Super Einwand. Danke!!

    kafffee schrieb:

    und da hab ich in meinem MainWindow

    Nur das du in der Startup das MainWindow der App-Projekts aufrufst und nicht das MainWindow des View-Projekts. Man sollte nie Elemente gleich benennen, so ist mal selbst nur verwirrt.

    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. ##

    @VaporiZed

    Ja hast glaub ich Recht. Ich hab das aus dem Kopf raus gemacht und mach das grad erst zum zweiten Mal, du sagst also da sollte im App-Projekt dann ein Fenster MainWindow sein
    welches dann ein UserControl MainView enthält oder?

    @Nofear23m
    Edit: Jetzt ham wir uns überschnitten. Ja klar klingt logisch, da hatte ich das falsch im Kopf mit der Struktur...
    Noch eine Zwischenfrage, bevor ich bezüglich der Services hier die Klappe halte: Warum soll der Dispatcher das mit dem Window.ShowDialog invoken? Gibt es Fälle, in denen das direkte Aufrufen wegen Nebenläufigkeit nicht funktioniert? Ist das ein realistisches Szenario?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Nur um das klar zu stellen

    Das Mainwindow im App-Projekt sollte NICHTS beinhalten, außer einem ContentControl. Und dieses Rendert dann den Inhalt (deine UserControls des View-Projekts) je nach DatenKontext was ja über die DataTemplates geregelt wird.

    Ich hoffe das meintest du.

    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. ##

    VaporiZed schrieb:

    Gibt es Fälle, in denen das direkte Aufrufen wegen Nebenläufigkeit nicht funktioniert? Ist das ein realistisches Szenario?

    Ja, beispielsweise wenn du eine Asyncrone Methode in deinem ViewModel hast.

    So hast du einfach den Vorteil das du im ViewModel nicht darauf achten musst.

    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:

    Das Mainwindow im App-Projekt sollte NICHTS beinhalten, außer einem ContentControl. Und dieses Rendert dann den Inhalt (deine UserControls des View-Projekts) je nach DatenKontext was ja über die DataTemplates geregelt wird.



    Du meinst aber, wenn MainWindow das Dialogfester sein soll, oder? Weil dann würde es für mich Sinn machen, da kann ich dann entweder einen OK-Button, oder z.B. Zwei Ja/Nein-Buttons reinmachen oder was auch immer ich brauche.

    In meinem Fall jetzt soll ja MainWindow nur zu Testzwecken dienen und den Button anzeigen, auf dessen Klick dann das Dialogfenster geöffnet wird...

    Und noch etwas:

    Mein Binding scheint nicht richtig zu funktionieren, ich hab das jetzt mit dem MainWindow bzw. MainView korrigiert, so dass mir jetzt mein Button zum Öffnen des Dialoges zwar angezeigt wird, aber auf Klicks nicht reagiert. Ich bin da noch ein bisschen unsicher mit dem XAML:

    XML-Quellcode

    1. <UserControl x:Class="MainView"
    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:DialogFensterTest.View"
    7. xmlns:View="clr-namespace:DialogFensterTest.View"
    8. xmlns:view=""
    9. xmlns:viewmodel="clr-namespace:DialogFensterTest.ViewModel;assembly=DialogFensterTest.ViewModel"
    10. mc:Ignorable="d"
    11. d:DesignHeight="450" d:DesignWidth="800">
    12. <UserControl.DataContext>
    13. <viewmodel:MainViewModel/>
    14. </UserControl.DataContext>
    15. <Grid>
    16. <Button Height="30" Width="100" Content="Dialog öffnen" Command="{Binding DialogOeffnen}"/>
    17. </Grid>
    18. </UserControl>


    Der Code im MainViewModel sieht so aus:

    VB.NET-Quellcode

    1. Imports System.Windows.Input
    2. Public Class MainViewModel
    3. Inherits ViewModel.Instrastructure.ViewModelBase
    4. Public Sub New()
    5. End Sub
    6. Private _DialogOeffnen As ICommand
    7. Public ReadOnly Property DialogOeffnen() As ICommand
    8. Get
    9. If _DialogOeffnen Is Nothing Then _DialogOeffnen = New ViewModel.Instrastructure.RelayCommand(AddressOf DialogOeffnen_Execute, Function(o) True)
    10. Return _DialogOeffnen
    11. End Get
    12. End Property
    13. Private Sub DialogOeffnen_Execute(obj As Object)
    14. Debug.WriteLine("Binding funzt!")
    15. Dim vm = New DialogFensterViewModel() 'Argument??
    16. Dim dialogService = Services.ServiceContainer.GetService(Of Services.IDialogWindowService)
    17. dialogService.ShowModalDialog("newAp", vm, Me, True, True)
    18. 'AccessPoints.Add(vm)
    19. End Sub
    20. End Class


    Und noch zwei Fragen, bevor ich weitermach:

    (1) Ist es möglich, das kleine Testprogramm dann in eine DLL umzuwandeln, so dass ich es leicht in künftige Projekte integrieren kann oder muss ich da von Anfang an irgendwas anders machen? In WinForms hab ich das schon gemacht, aber geht das auch mit einem MVVM-Projekt?

    (2) Wie mache ich es, wenn ich einen nicht-modalen Dialog haben will, ich habe vor, eine Fortschrittsanzeige zu machen, und da wäre es Unsinn, den Code anzuhalten, während der Dialog geöffnet ist?

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