.net - tutorial - wpf mvvm step by step




Gestion des boîtes de dialogue dans WPF avec MVVM (16)

Après avoir passé du temps avec, j'ai finalement trouvé la solution suivante. Quelques avantages clés de cette approche sont:

  1. Implémente IDialogService MVVM Light.
  2. La vue n'a pas besoin d'ajouter la référence de MVVM Light.
  3. VM n'a pas besoin d'effectuer d'activité au niveau de la présentation. N'a même pas besoin de référence PresentationFramework .
  4. Utilise le canal Messenger de MVVM Light, de sorte que les couches de présentation et de machine virtuelle sont découplées.
  5. Prend en charge les boîtes de dialogue avec une valeur de retour, telles que les questions Oui / Non ou OK / Annuler les situations.
  6. Prend en charge les boîtes de dialogue personnalisées.

Voici l'implémentation de IDialogService (va dans le projet ViewModel ):

using System;
using System.Linq;
using System.Threading.Tasks;

namespace VM
{
  public enum MessageBoxButtonVM
  {
    OK,
    OKCancel,
    YesNo
  }

  public enum MessageBoxImageVM
  {
    None,
    Information,
    Question,
    Error
  }

  public class MessageBoxArgs
  {
    public MessageBoxButtonVM Buttons { get; set; }
    public MessageBoxImageVM Icon { get; set; }
    public string Title { get; set; }
    public string Message { get; set; }
  }

  //For custom dialogs that return a value
  public class MessageBoxNotificationWithAction<T>
  {
    private readonly Action<T> _callback;

    public MessageBoxArgs Notification { get; set; }

    public MessageBoxNotificationWithAction(MessageBoxArgs notification, Action<T> callback)
    {
      Notification = notification;

      CheckCallback(callback);
      _callback = callback;
    }

    public virtual void Execute(T argument)
    {
      _callback.Invoke(argument);
    }

    private static void CheckCallback(Delegate callback)
    {
      if (callback == null)
      {
        throw new ArgumentNullException(nameof(callback), "Callback must not be null");
      }
    }
  }

  /// <summary>
  /// Provides an implementation-agnostic way of communicating with the user through dialog boxes. Clients must register for communication messages using
  /// MVVM Light messaging system.
  /// </summary>
  public class DialogService : GalaSoft.MvvmLight.Views.IDialogService
  {
    private static GalaSoft.MvvmLight.Messaging.IMessenger Messenger = GalaSoft.MvvmLight.Messaging.Messenger.Default;

    private string _ProductName = "";

    public string ProductName
    {
      get
      {
        if (_ProductName == "")
        {
          //The following statement returns the Title attribute of the current assembly, as defined in project properties (Assembly Information dialog).
          var TitleAttrib = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributesData().First(x => x.AttributeType.Name == "AssemblyTitleAttribute");

          if (TitleAttrib != null)
          {
            _ProductName = TitleAttrib.ConstructorArguments[0].Value.ToString();
          }
          else
          {
            _ProductName = "Default Application Name";
          }
        }

        return _ProductName;
      }
    }

    public Task ShowError(Exception error, string title, string buttonText, Action afterHideCallback)
    {
      return ShowError(error.Message, title, buttonText, afterHideCallback);
    }

    public Task ShowMessage(string message, string title)
    {
      return Task.Run(() => MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error));
    }

    public Task ShowError(string message, string title, string buttonText, Action afterHideCallback)
    {
      return Task.Run(() =>
      {
        MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error);
        afterHideCallback?.Invoke();
      });
    }

    public Task ShowMessage(string message, string title, string buttonText, Action afterHideCallback)
    {
      return Task.Run(() =>
      {
        MessengerSend(message, title);
        afterHideCallback?.Invoke();
      });
    }

    public Task<bool> ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action<bool> afterHideCallback)
    {
      if ((buttonConfirmText == "OK" && buttonCancelText == "Cancel") ||
        (buttonConfirmText == "Yes" && buttonCancelText == "No"))
      {
        return Task.Run<bool>(() =>
        {
          MessageBoxButtonVM btn;
          if (buttonConfirmText == "OK")
            btn = MessageBoxButtonVM.OKCancel;
          else
            btn = MessageBoxButtonVM.YesNo;


          bool Response = false;
          Messenger.Send(new MessageBoxNotificationWithAction<bool>(
                                                      new MessageBoxArgs()
                                                      {
                                                        Buttons = btn,
                                                        Icon = MessageBoxImageVM.Question,
                                                        Title = (string.IsNullOrEmpty(title) ? _ProductName : title),
                                                        Message = message
                                                      },
                                                      (result) => Response = result
                                                        ));

          afterHideCallback?.Invoke(Response);

          return Response;
        });
      }
      else
        throw new ArgumentException($"{nameof(buttonConfirmText)} and {nameof(buttonCancelText)} must either be OK/Cancel or Yes/No.");
    }

    /// <summary>
    /// For debugging purpose only
    /// </summary>
    /// <param name="message"></param>
    /// <param name="title"></param>
    /// <returns></returns>
    public Task ShowMessageBox(string message, string title) => ShowMessage(message, title);

    private void MessengerSend(string msg, string title = "", MessageBoxButtonVM btn = MessageBoxButtonVM.OK, MessageBoxImageVM icon = MessageBoxImageVM.Information)
    {
      Messenger.Send(new MessageBoxArgs()
      {
        Buttons = MessageBoxButtonVM.OK,
        Icon = MessageBoxImageVM.Information,
        Title = (string.IsNullOrEmpty(title) ? _ProductName : title),
        Message = msg
      });
    }
  }
}

Voici la couche de présentation (va dans le projet View )

using System.Windows;
using VM;

namespace View
{
  class DialogPresenter
  {
    private Window _Parent;

    public DialogPresenter()
    {
      //For simple information boxes
      GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<MessageBoxArgs>(this, (arg) => ShowDialog(arg));

      //For Yes/No or OK/Cancel dialog boxes.
      GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<MessageBoxNotificationWithAction<bool>>(this, (arg) => arg.Execute(ShowDialog(arg.Notification)));

      //For notifications that require a string response (such as Manual Timeslot Description)
      GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<MessageBoxNotificationWithAction<string>>(this,
        (arg) => arg.Execute(ShowStringInputDialog(arg.Notification.Title, arg.Notification.Message)));
    }

    private bool ShowDialog(MessageBoxArgs arg)
    {
      MessageBoxButton btn = MessageBoxButton.OK;
      MessageBoxImage ico = MessageBoxImage.None;

      switch (arg.Buttons)
      {
        case MessageBoxButtonVM.OK: btn = MessageBoxButton.OK; break;
        case MessageBoxButtonVM.OKCancel: btn = MessageBoxButton.OKCancel; break;
        case MessageBoxButtonVM.YesNo: btn = MessageBoxButton.YesNo; break;
      }

      switch (arg.Icon)
      {
        case MessageBoxImageVM.Error: ico = MessageBoxImage.Error; break;
        case MessageBoxImageVM.Information: ico = MessageBoxImage.Information; break;
        case MessageBoxImageVM.None: ico = MessageBoxImage.None; break;
        case MessageBoxImageVM.Question: ico = MessageBoxImage.Question; break;
      }

      bool Result = false;
      _Parent.Dispatcher.Invoke(() =>
      {
        var Res = MessageBox.Show(arg.Message, arg.Title, btn, ico);
        Result = (Res == MessageBoxResult.OK || Res == MessageBoxResult.Yes);
      });

      return Result;
    }

    private string ShowStringInputDialog(string title, string description, string value = "", int maxLength = 100)
    {
      string Result = null;

      _Parent.Dispatcher.Invoke(() =>
      {
        //InputBox is a WPF Window I created for taking simple
        //string values from the user. This also shows that you can
        //any custom dialog using this approach.

        InputBox input = new InputBox();
        input.Title = title;
        input.Owner = _Parent;
        if (input.ShowDialog(description, value, maxLength).Value)
          Result=input.Value;
        else
          Result=null;
      });

      return Result;
    }

    //Call this somewhere at application startup so that the dialog boxes
    //appear as child windows.
    public void SetParentWindow(Window parent)
    {
      _Parent = parent;
    }
  }
}

Dans le modèle MVVM pour WPF, la gestion des boîtes de dialogue est l'une des opérations les plus complexes. Comme votre modèle de vue ne sait rien de la vue, la communication par dialogue peut être intéressante. Je peux exposer une ICommand que lorsque la vue l'invoque, une boîte de dialogue peut apparaître.

Est-ce que quelqu'un sait d'un bon moyen de gérer les résultats des dialogues? Je parle de boîtes de dialogue Windows telles que MessageBox.

Une des façons dont nous avons fait ceci était d'avoir un événement sur le viewmodel auquel la vue s'abonnait quand une boîte de dialogue était requise.

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

C'est OK, mais cela signifie que la vue nécessite un code qui est quelque chose que je voudrais éviter.


Il y a deux bons moyens de le faire, 1) un service de dialogue (facile, propre), et 2) voir assisté. View assisté fournit des fonctionnalités intéressantes, mais ne vaut généralement pas la peine.

SERVICE DE DIALOGUE

a) une interface de service de dialogue comme via le constructeur ou un conteneur de dépendances:

interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }

b) Votre implémentation d'IDialogService doit ouvrir une fenêtre (ou injecter un peu de contrôle dans la fenêtre active), créer une vue correspondant au nom du type dlgVm donné (utiliser l'enregistrement ou la convention du conteneur ou un ContentPresenter avec le type DataTemplates associé). ShowDialogAsync doit créer une TaskCompletionSource et renvoyer sa propriété .Task. La classe DialogViewModel a elle-même besoin d'un événement que vous pouvez invoquer dans la classe dérivée lorsque vous voulez la fermer, et regardez dans la boîte de dialogue pour fermer / masquer la boîte de dialogue et compléter la TaskCompletionSource.

b) Pour l'utiliser, appelez simplement wait this.DialogService.ShowDialog (myDlgVm) sur votre instance d'une classe dérivée de DialogViewModel. Après l'attente des retours, regardez les propriétés que vous avez ajoutées sur votre machine virtuelle de dialogue pour déterminer ce qui s'est passé; vous n'avez même pas besoin d'un rappel.

VOIR ASSISTÉ

Cela a votre vue en écoutant un événement sur le viewmodel. Cela pourrait tout être enveloppé dans un comportement de mélange pour éviter le code derrière et l'utilisation des ressources si vous êtes si incliné (FMI, sous-classe la classe "Behavior" pour voir une sorte de propriété jointe Blendable sur les stéroïdes). Pour l'instant, nous le ferons manuellement sur chaque vue:

a) Créer un OpenXXXXXDialogEvent avec une charge utile personnalisée (une classe dérivée de DialogViewModel).

b) Faites en sorte que la vue s'abonne à l'événement dans son événement OnDataContextChanged. Assurez-vous de masquer et de vous désabonner si l'ancienne valeur! = Null et dans l'événement de Windows non déchargé.

c) Lorsque l'événement se déclenche, faites en sorte que la vue ouvre votre vue, qui pourrait être dans une ressource sur votre page, ou vous pouvez la localiser par convention ailleurs (comme dans l'approche du service de dialogue).

Cette approche est plus flexible, mais nécessite plus de travail à utiliser. Je ne l'utilise pas beaucoup. Le seul avantage est la possibilité de placer la vue physiquement dans un onglet, par exemple. J'ai utilisé un algorithme pour le placer dans les limites du contrôle utilisateur en cours, ou s'il n'est pas assez grand, il traverse l'arborescence visuelle jusqu'à ce qu'un conteneur assez grand soit trouvé.

Cela permet aux boîtes de dialogue d'être proches de l'endroit où elles sont réellement utilisées, d'atténuer la partie de l'application liée à l'activité en cours et de laisser l'utilisateur se déplacer dans l'application sans avoir à pousser manuellement les dialogues. les boîtes de dialogue modales s'ouvrent sur différents onglets ou sous-vues.



J'ai lutté avec le même problème. Je suis venu avec un moyen d'intercommuniquer entre la vue et le ViewModel. Vous pouvez lancer l'envoi d'un message du ViewModel à la vue pour lui indiquer d'afficher une boîte de message et il rendra compte du résultat. Ensuite, ViewModel peut répondre au résultat renvoyé par la vue.

Je le démontre dans mon blog :


J'ai vraiment lutté avec ce concept pendant un certain temps lors de l'apprentissage (encore en apprentissage) MVVM. Ce que j'ai décidé, et ce que je pense que d'autres ont déjà décidé mais qui n'était pas clair pour moi est la suivante:

Ma pensée originale était qu'un ViewModel ne devrait pas être autorisé à appeler une boîte de dialogue directement car il n'a pas d'affaires à décider comment une boîte de dialogue devrait apparaître. En raison de cela, j'ai commencé à réfléchir à la façon dont je pourrais transmettre des messages comme je le ferais dans MVP (ie View.ShowSaveFileDialog ()). Cependant, je pense que c'est la mauvaise approche.

Il est possible pour un ViewModel d'appeler une boîte de dialogue directement. Cependant, lorsque vous testez un ViewModel, cela signifie que la boîte de dialogue apparaîtra pendant votre test, ou échouera tous ensemble (jamais vraiment essayé).

Donc, ce qui doit arriver est pendant que le test est d'utiliser une version "test" de votre boîte de dialogue. Cela signifie que pour chaque dialogue que vous avez, vous devez créer une interface et soit simuler la réponse de la boîte de dialogue ou créer un test de simulation qui aura un comportement par défaut.

Vous devriez déjà utiliser une sorte de Service Locator ou IoC que vous pouvez configurer pour vous fournir la bonne version en fonction du contexte.

En utilisant cette approche, votre ViewModel est toujours testable et selon la façon dont vous raillez vos boîtes de dialogue, vous pouvez contrôler le comportement.

J'espère que cela t'aides.


J'utilise this approche pour les dialogues avec MVVM.

Tout ce que j'ai à faire maintenant est d'appeler ce qui suit à partir de mon modèle de vue.

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

Je pense que la vue pourrait avoir du code pour gérer l'événement à partir du modèle de vue.

En fonction de l'événement / scénario, il peut également avoir un déclencheur d'événement qui s'abonne pour voir les événements du modèle, et une ou plusieurs actions à invoquer en réponse.


Je réfléchissais à un problème similaire en demandant comment le modèle de vue pour une tâche ou un dialogue devrait ressembler .

Ma solution actuelle ressemble à ceci:

public class SelectionTaskModel<TChoosable> : ViewModel
    where TChoosable : ViewModel
{
    public SelectionTaskModel(ICollection<TChoosable> choices);
    public ReadOnlyCollection<TChoosable> Choices { get; }
    public void Choose(TChoosable choosen);
    public void Abort();
}

Lorsque le modèle de vue décide que l'entrée utilisateur est requise, il extrait une occurrence de SelectionTaskModel avec les choix possibles pour l'utilisateur. L'infrastructure prend soin d'afficher la vue correspondante, qui appellera en temps voulu la fonction Choose() au choix de l'utilisateur.


Je suggère de renoncer aux dialogues modaux des années 1990 et d'implémenter un contrôle en superposition (canvas + positionnement absolu) avec une visibilité liée à un retour booléen dans la machine virtuelle. Plus proche d'un contrôle de type ajax.

C'est très utile:

<BooleanToVisibilityConverter x:Key="booltoVis" />

un péché:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

Voici comment je l'ai implémenté en tant que contrôle utilisateur. Cliquer sur le 'x' ferme le contrôle dans une ligne de code dans le code d'usercontrol. (Depuis que j'ai mes vues dans un fichier .exe et ViewModels dans une DLL, je ne me sens pas mal à propos du code qui manipule l'interface utilisateur.)


Je suis peut-être de l'opinion très impopulaire selon laquelle se fier uniquement à XAML est, à votre avis, très limitatif. Je crois que nous pouvons mieux séparer View et ViewModel en utilisant le code-behind de la vue pour nous aider à implémenter la messagerie spécifique à la vue ou la collecte de données. En permettant au ViewModel d'exécuter sa propre logique et de renvoyer les résultats de telles propriétés, propriétés ou en tirant des événements, nous pouvons concevoir la vue pour faire presque tout ce que nous voulons (boîtes de dialogue incluses).

Dans un cas spécifique j'avais besoin d'une boîte de dialogue d'entrée de nom qui prend simplement le nom d'une famille et définit une propriété dans le ViewModel à utiliser plus tard. En raison de la quantité d'informations que j'avais besoin de collecter, avoir Dialogues pour attraper des choses comme des noms simplifié l'expérience de l'utilisateur assez que cela n'a pas l'air "écrasante".

Knowing this, I used a click event on the specific button to call a new custom dialog that I designed for this purpose. When calling the dialog I passed the parent Window's DataContext as an object in the constructor of the dialog. I then set the Dialog's DataContext via this parameter.

    public NameEntryDialog(object dataContext)
    {
        InitializeComponent();
        DataContext = dataContext;
    }

Now that the two views had a common DataContext I was able to set any property in the ViewModel by binding to the nodes of my dialog. This didn't require any major workarounds or loopholes to function properly.

Similarly, once data verification occurred in the ViewModel (No special characters or single character names, etc.) I had the ViewModel set two properties, 1) a validationSuccessful property and 2) an ErrorMessage property.

Once the dialog box was closed, my code-behind simply inspected those properties and called a MessageBox if validation was not successful (displaying the error message set in the property of my ViewModel). You could also accomplish this by subscribing to events in your ViewModel which, when fired, could call a Dialog or MessageBox in your view. I also had the logic of the ViewModel make the CanExecute property of the follow up "save" command "false" so that the user could not continue until those errors (and others during data collection) were corrected.

By using the code-behind I was able to completely separate the View from the ViewModel, and could also perform testing without the need for a view at all.

This also allows me to design new views which can interact with the same properties in the ViewModel independent of the need for a dialog. I could use a UserControl, a dialog, a single Window, etc. As long as I address all of the properties presented in my ViewModel, the sky is the limit. One thing that I've found to be true in almost every convoluted attempt at handling this problem was that the dialog box is eventually called SOMEWHERE (strong emphasis on somewhere) in code. Why not just skip all of that hassle and do it right inside your view?


Ma solution actuelle résout la plupart des problèmes que vous avez mentionnés mais elle est complètement abstraite des choses spécifiques à la plate-forme et peut être réutilisée. De plus, je n'ai utilisé aucune liaison par code uniquement avec DelegateCommands qui implémentent ICommand. La boîte de dialogue est fondamentalement une vue - un contrôle séparé qui a son propre ViewModel et il est affiché à partir du ViewModel de l'écran principal mais déclenché à partir de l'interface utilisateur via la liaison DelagateCommand.

Voir la solution complète Silverlight 4 ici Modal dialogues avec MVVM et Silverlight 4


Pourquoi ne pas simplement déclencher un événement dans la machine virtuelle et vous abonner à l'événement dans la vue? Cela garderait la logique de l'application et la vue séparée et vous permet toujours d'utiliser une fenêtre enfant pour les boîtes de dialogue.


Une bonne boîte de dialogue MVVM devrait:

  1. Être déclaré avec seulement XAML.
  2. Obtenez tout son comportement à partir de la liaison de données.

Malheureusement, WPF ne fournit pas ces fonctionnalités. L'affichage d'une boîte de dialogue nécessite un appel par code à ShowDialog (). La classe Window, qui prend en charge les boîtes de dialogue, ne peut pas être déclarée en XAML et ne peut donc pas être facilement liée à DataContext.

Pour résoudre ce problème, j'ai écrit un contrôle de talon XAML qui se trouve dans l'arbre logique et relaie la liaison de données à une fenêtre et gère l'affichage et le masquage de la boîte de dialogue. Vous pouvez le trouver ici: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

C'est vraiment simple à utiliser et ne nécessite pas de changements étranges à votre ViewModel et ne nécessite pas d'événements ou de messages. L'appel de base ressemble à ceci:

<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

Vous voulez probablement ajouter un style qui définit l'affichage. Je l'explique dans mon article. J'espère que ceci vous aide.


Utiliser une commande freezable

<Grid>
        <Grid.DataContext>
            <WpfApplication1:ViewModel />
        </Grid.DataContext>


        <Button Content="Text">
            <Button.Command>
                <WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
            </Button.Command>
        </Button>

</Grid>
public class MessageBoxCommand : Freezable, ICommand
{
    public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register(
        "YesCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register(
        "OKCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register(
        "CancelCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register(
        "NoCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
        "Message",
        typeof (string),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata("")
        );

    public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register(
        "MessageBoxButtons",
        typeof(MessageBoxButton),
        typeof(MessageBoxCommand),
        new FrameworkPropertyMetadata(MessageBoxButton.OKCancel)
        );

    public ICommand YesCommand
    {
        get { return (ICommand) GetValue(YesCommandProperty); }
        set { SetValue(YesCommandProperty, value); }
    }

    public ICommand OKCommand
    {
        get { return (ICommand) GetValue(OKCommandProperty); }
        set { SetValue(OKCommandProperty, value); }
    }

    public ICommand CancelCommand
    {
        get { return (ICommand) GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }

    public ICommand NoCommand
    {
        get { return (ICommand) GetValue(NoCommandProperty); }
        set { SetValue(NoCommandProperty, value); }
    }

    public MessageBoxButton MessageBoxButtons
    {
        get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); }
        set { SetValue(MessageBoxButtonsProperty, value); }
    }

    public string Message
    {
        get { return (string) GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public void Execute(object parameter)
    {
        var messageBoxResult = MessageBox.Show(Message);
        switch (messageBoxResult)
        {
            case MessageBoxResult.OK:
                OKCommand.Execute(null);
                break;
            case MessageBoxResult.Yes:
                YesCommand.Execute(null);
                break;
            case MessageBoxResult.No:
                NoCommand.Execute(null);
                break;
            case MessageBoxResult.Cancel:
                if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null
                break;

        }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;


    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}

EDIT: yes I agree this is not a correct MVVM approach and I am now using something similar to what is suggested by blindmeis.

One of the way you could to this is

In your Main View Model (where you open the modal):

void OpenModal()
{
    ModalWindowViewModel mwvm = new ModalWindowViewModel();
    Window mw = new Window();
    mw.content = mwvm;
    mw.ShowDialog()
    if(mw.DialogResult == true)
    { 
        // Your Code, you can access property in mwvm if you need.
    }
}

And in your Modal Window View/ViewModel:

XAML:

<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
<Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>

ViewModel:

public ICommand OkCommand
{
    get
    {
        if (_okCommand == null)
        {
            _okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
        }
        return _okCommand ;
    }
}

void DoOk(Window win)
{
    <!--Your Code-->
    win.DialogResult = true;
    win.Close();
}

bool CanDoOk(Window win) { return true; }

or similar to what is posted here WPF MVVM: How to close a window






dialog