web-dev-qa-db-fra.com

Implémentation de la commande "fermer la fenêtre" avec MVVM

Donc, ma première tentative a tout fait hors du code derrière, et maintenant j'essaye de refactoriser mon code pour utiliser le modèle MVVM, en suivant les indications du MVVM dans la boîte information. 

J'ai créé une classe viewmodel pour correspondre à ma classe, et je déplace le code du code précédent vers le modèle viewmodel en commençant par les commandes.

Mon premier problème est d'implémenter un bouton 'Fermer' qui ferme la fenêtre si les données n'ont pas été modifiées. J'ai configuré une CloseCommand pour remplacer la méthode 'onClick' et tout va bien, sauf pour les cas où le code tente de s'exécuter this.Close(). De toute évidence, étant donné que le code a été déplacé d'une fenêtre à une classe normale, "ceci" n'est pas une fenêtre et ne peut donc pas être fermé. Cependant, selon MVVM, le modèle de vue ne connaît pas la vue. Je ne peux donc pas appeler view.Close().

Quelqu'un peut-il suggérer comment je peux fermer la fenêtre à partir de la commande viewmodel?

35
mcalex

Vous n'avez pas besoin de transmettre l'instance View à votre couche ViewModel. Vous pouvez accéder à la fenêtre principale comme ceci -

Application.Current.MainWindow.Close()

Je ne vois aucun problème à accéder à votre fenêtre principale dans la classe ViewModel, comme indiqué ci-dessus. Conformément au principe MVVM, il ne devrait pas exister de lien étroit entre votre View et ViewModel, c’est-à-dire qu’ils devraient fonctionner sans se soucier des autres opérations. Ici, nous ne transmettons rien à ViewModel de View. Si vous souhaitez rechercher d'autres options, cela pourrait vous aider - Fermer la fenêtre avec MVVM

29
Rohit Vats

J'utilise personnellement une approche très simple: pour chaque ViewModel associé à une View pouvant être fermée, j'ai créé une ViewModel de base comme dans l'exemple suivant:

public abstract class CloseableViewModel
{
    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }
}

Ensuite, dans votre modèle de vue héritant de CloseableViewModel, appelez simplement this.OnClosingRequest(); pour la commande Close.

Dans la vue:

public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}
56
ken2k

Ma solution pour fermer une fenêtre du modèle de vue en cliquant sur un bouton est la suivante:

Dans le modèle 

public RelayCommand CloseWindow;
Constructor()
{
    CloseWindow = new RelayCommand(CloseWin);
}

public void CloseWin(object obj)
{
    Window win = obj as Window;
    win.Close();
}

Dans View, définissez comme suit

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />
25
PRASAD CP

Je le fais en créant une propriété attachée appelée DialogResult:

public static class DialogCloser
{
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window != null && (bool?)e.NewValue == true) 
                window.Close();
    }

    public static void SetDialogResult(Window target, bool? value)
    {
        target.SetValue(DialogResultProperty, value);
    }
}

puis écrivez ceci à vous XAML, dans la balise window

WindowActions:DialogCloser.DialogResult="{Binding Close}"

enfin dans le ViewModel 

    private bool _close;
    public bool Close
    {
        get { return _close; }
        set
        {
            if (_close == value)
                return;
            _close = value;
            NotifyPropertyChanged("Close");
        }
    }

si vous modifiez le fermer à vrai, la fenêtre sera fermée 

Close = True;
12
HB MAAM

Méfiez-vous des paradigmes à la mode. MVVM peut être utile, mais vous ne devriez vraiment pas le traiter comme un ensemble rigide de règles. Utilisez votre propre jugement, et quand cela n’a aucun sens, ne l’utilisez pas.

Les solutions fournies ici (à l'exception de la solution de @ RV1987) sont un très bon exemple de choses qui sortent des mains. Vous remplacez un seul appel Close() par une telle quantité de code, dans quel but? Vous ne gagnez absolument rien en déplaçant le code de fermeture de la vue vers la vue-modèle. La seule chose que vous gagnez est la place pour plus de bugs.

Maintenant, je ne dis pas que MVVM doit être ignoré. Bien au contraire, cela peut être très utile. Ne fais pas trop.

10
zmbq

Voici la solution la plus simple et la solution pure MVVM

Code ViewModel

public class ViewModel
{
    public Action CloseAction { get; set; }

    private void CloseCommandFunction()
    {
        CloseAction();
    }
}

Voici le code de vue XAML

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        ViewModel vm = new ViewModel();
        this.DataContext = vm;

        vm.CloseAction = new Action(() => this.Close());
    }
}
5
Krishna

Cette solution est rapide et facile. L'inconvénient est qu'il y a un certain couplage entre les couches. 

Dans votre vue modèle:

public class MyWindowViewModel: ViewModelBase
{


    public Command.StandardCommand CloseCommand
    {
        get
        {
            return new Command.StandardCommand(Close);
        }
    }
    public void Close()
    {
        foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
            }
        }
    }
}
5
eoldre

MVVM-light avec une notification de message personnalisée pour éviter la fenêtre permettant de traiter chaque message de notification

Dans le modèle de vue:

public class CloseDialogMessage : NotificationMessage
{
    public CloseDialogMessage(object sender) : base(sender, "") { }
}

private void OnClose()
{
    Messenger.Default.Send(new CloseDialogMessage(this));
}

Enregistrez le message dans le constructeur de fenêtre:

Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
    Close();
});
3
Eric Bole-Feysot

Ceci est très similaire à la réponse de eoldre. Son fonctionnement est identique en ce sens qu'il recherche dans la même collection Windows une fenêtre dont le modèle de vue est son contexte de données; mais j'ai utilisé un RelayCommand et un peu de LINQ pour obtenir le même résultat.

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}
2
Matt Winward

en utilisant MVVM-light toolkit:

Dans le ViewModel:

 public void notifyWindowToClose()
{
    Messenger.Default.Send<NotificationMessage>(
        new NotificationMessage(this, "CloseWindowsBoundToMe")
    );
}

Et dans la vue:

 Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});
2
David Fawzy

tout d'abord donner à votre fenêtre un nom comme 

x:Name="AboutViewWindow"

sur mon bouton de fermeture, j'ai défini Command and Command Parameter comme 

CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"

alors à mon avis modèle 

private ICommand _cancelCommand;        
public ICommand CancelCommand       
{
   get          
     {
        if (_cancelCommand == null)
           {
              _cancelCommand = new DelegateCommand<Window>(
                    x =>
                    {
                        x?.Close();
                    });
            }

            return _cancelCommand;          
     }      
}
0
dnxit

Ceci est tiré de la réponse de ken2k (merci!), En ajoutant simplement CloseCommand également à la base CloseableViewModel.

public class CloseableViewModel
{
    public CloseableViewModel()
    {
        CloseCommand = new RelayCommand(this.OnClosingRequest);
    }

    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }

    public RelayCommand CloseCommand
    {
        get;
        private set;
    }
}

Votre modèle de vue en hérite

public class MyViewModel : CloseableViewModel

Puis sur toi voir

public MyView()
{
    var viewModel = new StudyDataStructureViewModel(studyId);
    this.DataContext = viewModel;

    //InitializeComponent(); ...

    viewModel.ClosingRequest += (sender, e) => this.Close();
}
0
Chris Amelinckx

Compte tenu d'un chemin, s'il vous plaît vérifier 

https://stackoverflow.com/a/30546407/3659387

Brève description

  1. Dérivez votre ViewModel de INotifyPropertyChanged
  2. Créez une propriété observable CloseDialog dans ViewModel, modifiez la propriété CloseDialog chaque fois que vous souhaitez fermer la boîte de dialogue.
  3. Joindre un gestionnaire dans la vue pour ce changement de propriété
  4. Maintenant vous avez presque fini. Dans le gestionnaire d'événements, faites DialogResult = true
0
Anil8753